Implement date library functions in C++.

Developed together with Andreas Rossberg based on:
  https://chromiumcodereview.appspot.com/9117034/
  https://chromiumcodereview.appspot.com/9307083/

R=rossberg@chromium.org

Review URL: https://chromiumcodereview.appspot.com/9572008

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10983 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
ulan@chromium.org 2012-03-09 12:07:29 +00:00
parent 13d31b6594
commit 1767fef60b
36 changed files with 1528 additions and 1236 deletions

View File

@ -59,6 +59,7 @@ SOURCES = {
counters.cc
cpu-profiler.cc
data-flow.cc
date.cc
dateparser.cc
debug-agent.cc
debug.cc

View File

@ -4747,6 +4747,8 @@ void v8::Date::DateTimeConfigurationChangeNotification() {
LOG_API(isolate, "Date::DateTimeConfigurationChangeNotification");
ENTER_V8(isolate);
isolate->date_cache()->ResetDateCache();
i::HandleScope scope(isolate);
// Get the function ResetDateCache (defined in date.js).
i::Handle<i::String> func_name_str =

View File

@ -2951,17 +2951,42 @@ void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 2);
ASSERT_NE(NULL, args->at(1)->AsLiteral());
int index = Smi::cast(*(args->at(1)->AsLiteral()->handle()))->value();
Smi* index = Smi::cast(*(args->at(1)->AsLiteral()->handle()));
VisitForAccumulatorValue(args->at(0)); // Load the object.
Label runtime, done;
Register object = r0;
Register result = r0;
Register scratch0 = r9;
Register scratch1 = r1;
#ifdef DEBUG
__ AbortIfSmi(r0);
__ CompareObjectType(r0, r1, r1, JS_DATE_TYPE);
__ AbortIfSmi(object);
__ CompareObjectType(object, scratch1, scratch1, JS_DATE_TYPE);
__ Assert(eq, "Trying to get date field from non-date.");
#endif
__ ldr(r0, FieldMemOperand(r0, JSDate::kValueOffset + kPointerSize * index));
if (index->value() == 0) {
__ ldr(result, FieldMemOperand(object, JSDate::kValueOffset));
} else {
if (index->value() < JSDate::kFirstUncachedField) {
ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
__ mov(scratch1, Operand(stamp));
__ ldr(scratch1, MemOperand(scratch1));
__ ldr(scratch0, FieldMemOperand(object, JSDate::kCacheStampOffset));
__ cmp(scratch1, scratch0);
__ b(ne, &runtime);
__ ldr(result, FieldMemOperand(object, JSDate::kValueOffset +
kPointerSize * index->value()));
__ jmp(&done);
}
__ bind(&runtime);
__ PrepareCallCFunction(2, scratch1);
__ mov(r1, Operand(index));
__ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
__ bind(&done);
}
context()->Plug(r0);
}
@ -3010,37 +3035,6 @@ void FullCodeGenerator::EmitSetValueOf(CallRuntime* expr) {
}
void FullCodeGenerator::EmitSetDateField(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 3);
ASSERT_NE(NULL, args->at(1)->AsLiteral());
int index = Smi::cast(*(args->at(1)->AsLiteral()->handle()))->value();
VisitForStackValue(args->at(0)); // Load the object.
VisitForAccumulatorValue(args->at(2)); // Load the value.
__ pop(r1); // r0 = value. r1 = object.
#ifdef DEBUG
__ AbortIfSmi(r1);
__ CompareObjectType(r1, r2, r2, JS_DATE_TYPE);
__ Assert(eq, "Trying to get date field from non-date.");
#endif
// Store the value.
__ str(r0, FieldMemOperand(r1, JSDate::kValueOffset + kPointerSize * index));
// Caches can only be smi or NaN, so we can skip the write barrier for them.
if (index < JSDate::kFirstBarrierFree) {
// Update the write barrier. Save the value as it will be
// overwritten by the write barrier code and is needed afterward.
__ mov(r2, r0);
__ RecordWriteField(
r1, JSDate::kValueOffset + kPointerSize * index,
r2, r3, kLRHasBeenSaved, kDontSaveFPRegs);
}
context()->Plug(r0);
}
void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT_EQ(args->length(), 1);

View File

@ -1603,18 +1603,9 @@ LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
LOperand* object = UseRegister(instr->value());
LDateField* result = new LDateField(object, TempRegister(), instr->index());
return DefineAsRegister(result);
}
LInstruction* LChunkBuilder::DoSetDateField(HSetDateField* instr) {
LOperand* date = UseTempRegister(instr->OperandAt(1));
LOperand* value = UseTempRegister(instr->OperandAt(2));
LSetDateField* result =
new LSetDateField(date, value, TempRegister(), instr->index());
return DefineAsRegister(result);
LOperand* object = UseFixed(instr->value(), r0);
LDateField* result = new LDateField(object, FixedTemp(r1), instr->index());
return MarkAsCall(DefineFixed(result, r0), instr);
}

View File

@ -178,8 +178,7 @@ class LCodeGen;
V(ForInCacheArray) \
V(CheckMapValue) \
V(LoadFieldByIndex) \
V(DateField) \
V(SetDateField)
V(DateField)
#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
@ -993,17 +992,17 @@ class LValueOf: public LTemplateInstruction<1, 1, 1> {
class LDateField: public LTemplateInstruction<1, 1, 1> {
public:
LDateField(LOperand* date, LOperand* temp, int index) : index_(index) {
LDateField(LOperand* date, LOperand* temp, Smi* index) : index_(index) {
inputs_[0] = date;
temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(ValueOf, "date-field")
DECLARE_HYDROGEN_ACCESSOR(ValueOf)
int index() const { return index_; }
Smi* index() const { return index_; }
private:
int index_;
Smi* index_;
};

View File

@ -1439,44 +1439,41 @@ void LCodeGen::DoValueOf(LValueOf* instr) {
void LCodeGen::DoDateField(LDateField* instr) {
Register input = ToRegister(instr->InputAt(0));
Register object = ToRegister(instr->InputAt(0));
Register result = ToRegister(instr->result());
Register map = ToRegister(instr->TempAt(0));
Register scratch = ToRegister(instr->TempAt(0));
Smi* index = instr->index();
Label runtime, done;
ASSERT(object.is(result));
ASSERT(object.is(r0));
ASSERT(!scratch.is(scratch0()));
ASSERT(!scratch.is(object));
#ifdef DEBUG
__ AbortIfSmi(input);
__ CompareObjectType(input, map, map, JS_DATE_TYPE);
__ AbortIfSmi(object);
__ CompareObjectType(object, scratch, scratch, JS_DATE_TYPE);
__ Assert(eq, "Trying to get date field from non-date.");
#endif
__ ldr(result, FieldMemOperand(input,
JSDate::kValueOffset + kPointerSize * instr->index()));
}
void LCodeGen::DoSetDateField(LSetDateField* instr) {
Register date = ToRegister(instr->InputAt(0));
Register value = ToRegister(instr->InputAt(1));
Register result = ToRegister(instr->result());
Register temp = ToRegister(instr->TempAt(0));
int index = instr->index();
#ifdef DEBUG
__ AbortIfSmi(date);
__ CompareObjectType(date, temp, temp, JS_DATE_TYPE);
__ Assert(eq, "Trying to get date field from non-date.");
#endif
__ str(value,
FieldMemOperand(date, JSDate::kValueOffset + kPointerSize * index));
// Caches can only be smi or NaN, so we can skip the write barrier for them.
if (index < JSDate::kFirstBarrierFree) {
// Update the write barrier. Save the value as it will be
// overwritten by the write barrier code and is needed afterward.
__ mov(result, value);
__ RecordWriteField(
date, JSDate::kValueOffset + kPointerSize * index,
value, temp, kLRHasBeenSaved, kDontSaveFPRegs);
if (index->value() == 0) {
__ ldr(result, FieldMemOperand(object, JSDate::kValueOffset));
} else {
if (index->value() < JSDate::kFirstUncachedField) {
ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
__ mov(scratch, Operand(stamp));
__ ldr(scratch, MemOperand(scratch));
__ ldr(scratch0(), FieldMemOperand(object, JSDate::kCacheStampOffset));
__ cmp(scratch, scratch0());
__ b(ne, &runtime);
__ ldr(result, FieldMemOperand(object, JSDate::kValueOffset +
kPointerSize * index->value()));
__ jmp(&done);
}
__ bind(&runtime);
__ PrepareCallCFunction(2, scratch);
__ mov(r1, Operand(index));
__ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
__ bind(&done);
}
}

View File

@ -813,6 +813,17 @@ ExternalReference ExternalReference::random_uint32_function(
}
ExternalReference ExternalReference::get_date_field_function(
Isolate* isolate) {
return ExternalReference(Redirect(isolate, FUNCTION_ADDR(JSDate::GetField)));
}
ExternalReference ExternalReference::date_cache_stamp(Isolate* isolate) {
return ExternalReference(isolate->date_cache()->stamp_address());
}
ExternalReference ExternalReference::transcendental_cache_array_address(
Isolate* isolate) {
return ExternalReference(

View File

@ -595,6 +595,9 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference transcendental_cache_array_address(Isolate* isolate);
static ExternalReference delete_handle_scope_extensions(Isolate* isolate);
static ExternalReference get_date_field_function(Isolate* isolate);
static ExternalReference date_cache_stamp(Isolate* isolate);
// Deoptimization support.
static ExternalReference new_deoptimizer_function(Isolate* isolate);
static ExternalReference compute_output_frames_function(Isolate* isolate);

384
src/date.cc Normal file
View File

@ -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

254
src/date.h Normal file
View File

@ -0,0 +1,254 @@
// 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;
// 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) {
return static_cast<int>(OS::DaylightSavingsOffset(time_sec * 1000));
}
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 Load Diff

View File

@ -185,8 +185,7 @@ class LChunkBuilder;
V(ForInCacheArray) \
V(CheckMapValue) \
V(LoadFieldByIndex) \
V(DateField) \
V(SetDateField)
V(DateField)
#define GVN_FLAG_LIST(V) \
V(Calls) \
@ -4606,11 +4605,12 @@ class HValueOf: public HUnaryOperation {
class HDateField: public HUnaryOperation {
public:
HDateField(HValue* date, int index) : HUnaryOperation(date), index_(index) {
HDateField(HValue* date, Smi* index)
: HUnaryOperation(date), index_(index) {
set_representation(Representation::Tagged());
}
int index() const { return index_; }
Smi* index() const { return index_; }
virtual Representation RequiredInputRepresentation(int index) {
return Representation::Tagged();
@ -4619,27 +4619,7 @@ class HDateField: public HUnaryOperation {
DECLARE_CONCRETE_INSTRUCTION(DateField)
private:
int index_;
};
class HSetDateField: public HBinaryOperation {
public:
HSetDateField(HValue* context, HValue* date, HValue* value, int index)
: HBinaryOperation(context, date, value), index_(index) {
set_representation(Representation::Tagged());
}
int index() const { return index_; }
virtual Representation RequiredInputRepresentation(int index) {
return Representation::Tagged();
}
DECLARE_CONCRETE_INSTRUCTION(SetDateField)
private:
int index_;
Smi* index_;
};

View File

@ -7125,8 +7125,7 @@ void HGraphBuilder::GenerateValueOf(CallRuntime* call) {
void HGraphBuilder::GenerateDateField(CallRuntime* call) {
ASSERT(call->arguments()->length() == 2);
ASSERT_NE(NULL, call->arguments()->at(1)->AsLiteral());
int index =
Smi::cast(*(call->arguments()->at(1)->AsLiteral()->handle()))->value();
Smi* index = Smi::cast(*(call->arguments()->at(1)->AsLiteral()->handle()));
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* date = Pop();
HDateField* result = new(zone()) HDateField(date, index);
@ -7176,22 +7175,6 @@ void HGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
}
void HGraphBuilder::GenerateSetDateField(CallRuntime* call) {
ASSERT(call->arguments()->length() == 3);
ASSERT_NE(NULL, call->arguments()->at(1)->AsLiteral());
int index =
Smi::cast(*(call->arguments()->at(1)->AsLiteral()->handle()))->value();
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
CHECK_ALIVE(VisitForValue(call->arguments()->at(2)));
HValue* value = Pop();
HValue* date = Pop();
HValue* context = environment()->LookupContext();
HSetDateField* result =
new(zone()) HSetDateField(context, date, value, index);
return ast_context()->ReturnInstruction(result, call->id());
}
// Fast support for charCodeAt(n).
void HGraphBuilder::GenerateStringCharCodeAt(CallRuntime* call) {
ASSERT(call->arguments()->length() == 2);

View File

@ -2965,18 +2965,41 @@ void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 2);
ASSERT_NE(NULL, args->at(1)->AsLiteral());
int index = Smi::cast(*(args->at(1)->AsLiteral()->handle()))->value();
Smi* index = Smi::cast(*(args->at(1)->AsLiteral()->handle()));
VisitForAccumulatorValue(args->at(0)); // Load the object.
Label runtime, done;
Register object = eax;
Register result = eax;
Register scratch = ecx;
#ifdef DEBUG
__ AbortIfSmi(eax);
__ CmpObjectType(eax, JS_DATE_TYPE, ebx);
__ AbortIfSmi(object);
__ CmpObjectType(object, JS_DATE_TYPE, scratch);
__ Assert(equal, "Trying to get date field from non-date.");
#endif
__ mov(eax, FieldOperand(eax, JSDate::kValueOffset + kPointerSize * index));
context()->Plug(eax);
if (index->value() == 0) {
__ mov(result, FieldOperand(object, JSDate::kValueOffset));
} else {
if (index->value() < JSDate::kFirstUncachedField) {
ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
__ mov(scratch, Operand::StaticVariable(stamp));
__ cmp(scratch, FieldOperand(object, JSDate::kCacheStampOffset));
__ j(not_equal, &runtime, Label::kNear);
__ mov(result, FieldOperand(object, JSDate::kValueOffset +
kPointerSize * index->value()));
__ jmp(&done);
}
__ bind(&runtime);
__ PrepareCallCFunction(2, scratch);
__ mov(Operand(esp, 0), object);
__ mov(Operand(esp, 1 * kPointerSize), Immediate(index));
__ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
__ bind(&done);
}
context()->Plug(result);
}
@ -3026,36 +3049,6 @@ void FullCodeGenerator::EmitSetValueOf(CallRuntime* expr) {
}
void FullCodeGenerator::EmitSetDateField(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 3);
ASSERT_NE(NULL, args->at(1)->AsLiteral());
int index = Smi::cast(*(args->at(1)->AsLiteral()->handle()))->value();
VisitForStackValue(args->at(0)); // Load the object.
VisitForAccumulatorValue(args->at(2)); // Load the value.
__ pop(ebx); // eax = value. ebx = object.
#ifdef DEBUG
__ AbortIfSmi(ebx);
__ CmpObjectType(ebx, JS_DATE_TYPE, ecx);
__ Assert(equal, "Trying to set date field on non-date.");
#endif
// Store the value.
__ mov(FieldOperand(ebx, JSDate::kValueOffset + kPointerSize * index), eax);
// Caches can only be smi or NaN, so we can skip the write barrier for them.
if (index < JSDate::kFirstBarrierFree) {
// Update the write barrier. Save the value as it will be
// overwritten by the write barrier code and is needed afterward.
__ mov(edx, eax);
__ RecordWriteField(ebx, JSDate::kValueOffset + kPointerSize * index,
edx, ecx, kDontSaveFPRegs);
}
context()->Plug(eax);
}
void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT_EQ(args->length(), 1);

View File

@ -1288,44 +1288,38 @@ void LCodeGen::DoValueOf(LValueOf* instr) {
void LCodeGen::DoDateField(LDateField* instr) {
Register input = ToRegister(instr->InputAt(0));
Register object = ToRegister(instr->InputAt(0));
Register result = ToRegister(instr->result());
Register map = ToRegister(instr->TempAt(0));
ASSERT(input.is(result));
Register scratch = ToRegister(instr->TempAt(0));
Smi* index = instr->index();
Label runtime, done;
ASSERT(object.is(result));
ASSERT(object.is(eax));
#ifdef DEBUG
__ AbortIfSmi(input);
__ CmpObjectType(input, JS_DATE_TYPE, map);
__ AbortIfSmi(object);
__ CmpObjectType(object, JS_DATE_TYPE, scratch);
__ Assert(equal, "Trying to get date field from non-date.");
#endif
__ mov(result, FieldOperand(input,
JSDate::kValueOffset + kPointerSize * instr->index()));
}
void LCodeGen::DoSetDateField(LSetDateField* instr) {
Register date = ToRegister(instr->InputAt(0));
Register value = ToRegister(instr->InputAt(1));
Register result = ToRegister(instr->result());
Register temp = ToRegister(instr->TempAt(0));
int index = instr->index();
#ifdef DEBUG
__ AbortIfSmi(date);
__ CmpObjectType(date, JS_DATE_TYPE, temp);
__ Assert(equal, "Trying to get date field from non-date.");
#endif
__ mov(FieldOperand(date, JSDate::kValueOffset + kPointerSize * index),
value);
// Caches can only be smi or NaN, so we can skip the write barrier for them.
if (index < JSDate::kFirstBarrierFree) {
// Update the write barrier. Save the value as it will be
// overwritten by the write barrier code and is needed afterward.
__ mov(result, value);
__ RecordWriteField(date, JSDate::kValueOffset + kPointerSize * index,
value, temp, kDontSaveFPRegs);
if (index->value() == 0) {
__ mov(result, FieldOperand(object, JSDate::kValueOffset));
} else {
if (index->value() < JSDate::kFirstUncachedField) {
ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
__ mov(scratch, Operand::StaticVariable(stamp));
__ cmp(scratch, FieldOperand(object, JSDate::kCacheStampOffset));
__ j(not_equal, &runtime, Label::kNear);
__ mov(result, FieldOperand(object, JSDate::kValueOffset +
kPointerSize * index->value()));
__ jmp(&done);
}
__ bind(&runtime);
__ PrepareCallCFunction(2, scratch);
__ mov(Operand(esp, 0), object);
__ mov(Operand(esp, 1 * kPointerSize), Immediate(index));
__ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
__ bind(&done);
}
}

View File

@ -1648,19 +1648,10 @@ LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
LOperand* date = UseRegister(instr->value());
LOperand* date = UseFixed(instr->value(), eax);
LDateField* result =
new(zone()) LDateField(date, TempRegister(), instr->index());
return DefineSameAsFirst(result);
}
LInstruction* LChunkBuilder::DoSetDateField(HSetDateField* instr) {
LOperand* date = UseTempRegister(instr->OperandAt(1));
LOperand* value = UseTempRegister(instr->OperandAt(2));
LSetDateField* result =
new(zone()) LSetDateField(date, value, TempRegister(), instr->index());
return DefineAsRegister(result);
new(zone()) LDateField(date, FixedTemp(ecx), instr->index());
return MarkAsCall(DefineFixed(result, eax), instr);
}

View File

@ -173,8 +173,7 @@ class LCodeGen;
V(ForInCacheArray) \
V(CheckMapValue) \
V(LoadFieldByIndex) \
V(DateField) \
V(SetDateField)
V(DateField)
#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
@ -1006,7 +1005,8 @@ class LValueOf: public LTemplateInstruction<1, 1, 1> {
class LDateField: public LTemplateInstruction<1, 1, 1> {
public:
LDateField(LOperand* date, LOperand* temp, int index) : index_(index) {
LDateField(LOperand* date, LOperand* temp, Smi* index)
: index_(index) {
inputs_[0] = date;
temps_[0] = temp;
}
@ -1014,29 +1014,10 @@ class LDateField: public LTemplateInstruction<1, 1, 1> {
DECLARE_CONCRETE_INSTRUCTION(DateField, "date-field")
DECLARE_HYDROGEN_ACCESSOR(DateField)
int index() const { return index_; }
Smi* index() const { return index_; }
private:
int index_;
};
class LSetDateField: public LTemplateInstruction<1, 2, 1> {
public:
LSetDateField(LOperand* date, LOperand* value, LOperand* temp, int index)
: index_(index) {
inputs_[0] = date;
inputs_[1] = value;
temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(DateField, "date-set-field")
DECLARE_HYDROGEN_ACCESSOR(DateField)
int index() const { return index_; }
private:
int index_;
Smi* index_;
};

View File

@ -1486,6 +1486,7 @@ Isolate::Isolate()
has_installed_extensions_(false),
string_tracker_(NULL),
regexp_stack_(NULL),
date_cache_(NULL),
embedder_data_(NULL),
context_exit_happened_(false) {
TRACE_ISOLATE(constructor);
@ -1618,6 +1619,9 @@ Isolate::~Isolate() {
delete unicode_cache_;
unicode_cache_ = NULL;
delete date_cache_;
date_cache_ = NULL;
delete regexp_stack_;
regexp_stack_ = NULL;
@ -1782,6 +1786,7 @@ bool Isolate::Init(Deserializer* des) {
stub_cache_ = new StubCache(this);
regexp_stack_ = new RegExpStack();
regexp_stack_->isolate_ = this;
date_cache_ = new DateCache();
// Enable logging before setting up the heap
logger_->SetUp();

View File

@ -36,6 +36,7 @@
#include "contexts.h"
#include "execution.h"
#include "frames.h"
#include "date.h"
#include "global-handles.h"
#include "handles.h"
#include "hashmap.h"
@ -1017,6 +1018,17 @@ class Isolate {
return OS::TimeCurrentMillis() - time_millis_at_init_;
}
DateCache* date_cache() {
return date_cache_;
}
void set_date_cache(DateCache* date_cache) {
if (date_cache != date_cache_) {
delete date_cache_;
}
date_cache_ = date_cache;
}
private:
Isolate();
@ -1184,6 +1196,9 @@ class Isolate {
unibrow::Mapping<unibrow::Ecma262Canonicalize>
regexp_macro_assembler_canonicalize_;
RegExpStack* regexp_stack_;
DateCache* date_cache_;
unibrow::Mapping<unibrow::Ecma262Canonicalize> interp_canonicalize_mapping_;
void* embedder_data_;

View File

@ -164,35 +164,36 @@ const MAX_TIME_BEFORE_UTC = 8640002592000000;
# Gets the value of a Date object. If arg is not a Date object
# a type error is thrown.
macro DATE_VALUE_UNCHECKED(arg) = (%_DateField(arg, 0));
macro DATE_LOCAL_UNCHECKED(arg) = (%_DateField(arg, 1));
macro DATE_VALUE(arg) = (%_ClassOf(arg) === 'Date' ? %_DateField(arg, 0) : ThrowDateTypeError());
macro DATE_LOCAL(arg) = (%_ClassOf(arg) === 'Date' ? %_DateField(arg, 1) : ThrowDateTypeError());
macro DATE_YEAR(arg) = (%_ClassOf(arg) === 'Date' ? %_DateField(arg, 2) : ThrowDateTypeError());
macro DATE_MONTH(arg) = (%_ClassOf(arg) === 'Date' ? %_DateField(arg, 3) : ThrowDateTypeError());
macro DATE_DAY(arg) = (%_ClassOf(arg) === 'Date' ? %_DateField(arg, 4) : ThrowDateTypeError());
macro DATE_HOUR(arg) = (%_ClassOf(arg) === 'Date' ? %_DateField(arg, 5) : ThrowDateTypeError());
macro DATE_MIN(arg) = (%_ClassOf(arg) === 'Date' ? %_DateField(arg, 6) : ThrowDateTypeError());
macro DATE_SEC(arg) = (%_ClassOf(arg) === 'Date' ? %_DateField(arg, 7) : ThrowDateTypeError());
macro DATE_WEEKDAY(arg) = (%_ClassOf(arg) === 'Date' ? %_DateField(arg, 8) : ThrowDateTypeError());
macro SET_DATE_VALUE(arg, value) = (%_SetDateField(arg, 0, value));
macro SET_DATE_LOCAL(arg, value) = (%_SetDateField(arg, 1, value));
macro SET_DATE_YEAR(arg, value) = (%_SetDateField(arg, 2, value));
macro SET_DATE_MONTH(arg, value) = (%_SetDateField(arg, 3, value));
macro SET_DATE_DAY(arg, value) = (%_SetDateField(arg, 4, value));
macro SET_DATE_HOUR(arg, value) = (%_SetDateField(arg, 5, value));
macro SET_DATE_MIN(arg, value) = (%_SetDateField(arg, 6, value));
macro SET_DATE_SEC(arg, value) = (%_SetDateField(arg, 7, value));
macro SET_DATE_WEEKDAY(arg, value) = (%_SetDateField(arg, 8, value));
macro DAY(time) = ($floor(time / 86400000));
macro NAN_OR_DATE_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : DateFromTime(time));
macro HOUR_FROM_TIME(time) = (Modulo($floor(time / 3600000), 24));
macro MIN_FROM_TIME(time) = (Modulo($floor(time / 60000), 60));
macro NAN_OR_MIN_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : MIN_FROM_TIME(time));
macro SEC_FROM_TIME(time) = (Modulo($floor(time / 1000), 60));
macro NAN_OR_SEC_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : SEC_FROM_TIME(time));
macro MS_FROM_TIME(time) = (Modulo(time, 1000));
macro NAN_OR_MS_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : MS_FROM_TIME(time));
macro CHECK_DATE(arg) = if (%_ClassOf(arg) !== 'Date') ThrowDateTypeError();
macro LOCAL_DATE_VALUE(arg) = (%_DateField(arg, 0) + %_DateField(arg, 21));
macro UTC_DATE_VALUE(arg) = (%_DateField(arg, 0));
macro LOCAL_YEAR(arg) = (%_DateField(arg, 1));
macro LOCAL_MONTH(arg) = (%_DateField(arg, 2));
macro LOCAL_DAY(arg) = (%_DateField(arg, 3));
macro LOCAL_WEEKDAY(arg) = (%_DateField(arg, 4));
macro LOCAL_HOUR(arg) = (%_DateField(arg, 5));
macro LOCAL_MIN(arg) = (%_DateField(arg, 6));
macro LOCAL_SEC(arg) = (%_DateField(arg, 7));
macro LOCAL_MS(arg) = (%_DateField(arg, 8));
macro LOCAL_DAYS(arg) = (%_DateField(arg, 9));
macro LOCAL_TIME_IN_DAY(arg) = (%_DateField(arg, 10));
macro UTC_YEAR(arg) = (%_DateField(arg, 11));
macro UTC_MONTH(arg) = (%_DateField(arg, 12));
macro UTC_DAY(arg) = (%_DateField(arg, 13));
macro UTC_WEEKDAY(arg) = (%_DateField(arg, 14));
macro UTC_HOUR(arg) = (%_DateField(arg, 15));
macro UTC_MIN(arg) = (%_DateField(arg, 16));
macro UTC_SEC(arg) = (%_DateField(arg, 17));
macro UTC_MS(arg) = (%_DateField(arg, 18));
macro UTC_DAYS(arg) = (%_DateField(arg, 19));
macro UTC_TIME_IN_DAY(arg) = (%_DateField(arg, 20));
macro TIMEZONE_OFFSET(arg) = (%_DateField(arg, 21));
macro SET_UTC_DATE_VALUE(arg, value) = (%DateSetValue(arg, value, 1));
macro SET_LOCAL_DATE_VALUE(arg, value) = (%DateSetValue(arg, value, 0));
# Last input and last subject of regexp matches.
macro LAST_SUBJECT(array) = ((array)[1]);

View File

@ -378,18 +378,18 @@ void JSDate::JSDateVerify() {
if (value()->IsHeapObject()) {
VerifyHeapPointer(value());
}
if (local()->IsHeapObject()) {
VerifyHeapPointer(local());
}
CHECK(value()->IsUndefined() || value()->IsSmi() || value()->IsHeapNumber());
CHECK(local()->IsUndefined() || local()->IsSmi() || local()->IsHeapNumber());
CHECK(year()->IsUndefined() || year()->IsSmi() || year()->IsNaN());
CHECK(month()->IsUndefined() || month()->IsSmi() || month()->IsNaN());
CHECK(day()->IsUndefined() || day()->IsSmi() || day()->IsNaN());
CHECK(weekday()->IsUndefined() || weekday()->IsSmi() || weekday()->IsNaN());
CHECK(hour()->IsUndefined() || hour()->IsSmi() || hour()->IsNaN());
CHECK(min()->IsUndefined() || min()->IsSmi() || min()->IsNaN());
CHECK(sec()->IsUndefined() || sec()->IsSmi() || sec()->IsNaN());
CHECK(weekday()->IsUndefined() || weekday()->IsSmi() || weekday()->IsNaN());
CHECK(cache_stamp()->IsUndefined() ||
cache_stamp()->IsSmi() ||
cache_stamp()->IsNaN());
if (month()->IsSmi()) {
int month = Smi::cast(this->month())->value();
CHECK(0 <= month && month <= 11);
@ -414,6 +414,10 @@ void JSDate::JSDateVerify() {
int weekday = Smi::cast(this->weekday())->value();
CHECK(0 <= weekday && weekday <= 6);
}
if (cache_stamp()->IsSmi()) {
CHECK(Smi::cast(cache_stamp())->value() <=
Smi::cast(Isolate::Current()->date_cache()->stamp())->value());
}
}

View File

@ -4128,14 +4128,14 @@ JSValue* JSValue::cast(Object* obj) {
ACCESSORS(JSDate, value, Object, kValueOffset)
ACCESSORS(JSDate, local, Object, kLocalOffset)
ACCESSORS(JSDate, cache_stamp, Object, kCacheStampOffset)
ACCESSORS(JSDate, year, Object, kYearOffset)
ACCESSORS(JSDate, month, Object, kMonthOffset)
ACCESSORS(JSDate, day, Object, kDayOffset)
ACCESSORS(JSDate, weekday, Object, kWeekdayOffset)
ACCESSORS(JSDate, hour, Object, kHourOffset)
ACCESSORS(JSDate, min, Object, kMinOffset)
ACCESSORS(JSDate, sec, Object, kSecOffset)
ACCESSORS(JSDate, weekday, Object, kWeekdayOffset)
JSDate* JSDate::cast(Object* obj) {

View File

@ -33,6 +33,7 @@
#include "codegen.h"
#include "debug.h"
#include "deoptimizer.h"
#include "date.h"
#include "elements.h"
#include "execution.h"
#include "full-codegen.h"
@ -12970,4 +12971,133 @@ int BreakPointInfo::GetBreakPointCount() {
#endif // ENABLE_DEBUGGER_SUPPORT
MaybeObject* JSDate::GetField(Object* object, Smi* index) {
return JSDate::cast(object)->DoGetField(
static_cast<FieldIndex>(index->value()));
}
Object* JSDate::DoGetField(FieldIndex index) {
ASSERT(index != kDateValue);
DateCache* date_cache = GetIsolate()->date_cache();
if (index < kFirstUncachedField) {
Object* stamp = cache_stamp();
if (stamp != date_cache->stamp() && stamp->IsSmi()) {
// Since the stamp is not NaN, the value is also not NaN.
int64_t local_time_ms =
static_cast<int64_t>(date_cache->ToLocal(value()->Number()));
SetLocalFields(local_time_ms, date_cache);
}
switch (index) {
case kYear: return year();
case kMonth: return month();
case kDay: return day();
case kWeekday: return weekday();
case kHour: return hour();
case kMinute: return min();
case kSecond: return sec();
default: UNREACHABLE();
}
}
if (index >= kFirstUTCField) {
return GetUTCField(index, value()->Number(), date_cache);
}
double time = value()->Number();
if (isnan(time)) return GetIsolate()->heap()->nan_value();
int64_t local_time_ms = static_cast<int64_t>(date_cache->ToLocal(time));
int days = DateCache::DaysFromTime(local_time_ms);
if (index == kDays) return Smi::FromInt(days);
int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days);
if (index == kMillisecond) return Smi::FromInt(time_in_day_ms % 1000);
ASSERT(index == kTimeInDay);
return Smi::FromInt(time_in_day_ms);
}
Object* JSDate::GetUTCField(FieldIndex index,
double value,
DateCache* date_cache) {
ASSERT(index >= kFirstUTCField);
if (isnan(value)) return GetIsolate()->heap()->nan_value();
int64_t time_ms = static_cast<int64_t>(value);
if (index == kTimezoneOffset) {
return Smi::FromInt(date_cache->TimezoneOffset(time_ms));
}
int days = DateCache::DaysFromTime(time_ms);
if (index == kWeekdayUTC) return Smi::FromInt(date_cache->Weekday(days));
if (index <= kDayUTC) {
int year, month, day;
date_cache->YearMonthDayFromDays(days, &year, &month, &day);
if (index == kYearUTC) return Smi::FromInt(year);
if (index == kMonthUTC) return Smi::FromInt(month);
ASSERT(index == kDayUTC);
return Smi::FromInt(day);
}
int time_in_day_ms = DateCache::TimeInDay(time_ms, days);
switch (index) {
case kHourUTC: return Smi::FromInt(time_in_day_ms / (60 * 60 * 1000));
case kMinuteUTC: return Smi::FromInt((time_in_day_ms / (60 * 1000)) % 60);
case kSecondUTC: return Smi::FromInt((time_in_day_ms / 1000) % 60);
case kMillisecondUTC: return Smi::FromInt(time_in_day_ms % 1000);
case kDaysUTC: return Smi::FromInt(days);
case kTimeInDayUTC: return Smi::FromInt(time_in_day_ms);
default: UNREACHABLE();
}
UNREACHABLE();
return NULL;
}
void JSDate::SetValue(Object* value, bool is_value_nan) {
set_value(value);
if (is_value_nan) {
HeapNumber* nan = GetIsolate()->heap()->nan_value();
set_cache_stamp(nan, SKIP_WRITE_BARRIER);
set_year(nan, SKIP_WRITE_BARRIER);
set_month(nan, SKIP_WRITE_BARRIER);
set_day(nan, SKIP_WRITE_BARRIER);
set_hour(nan, SKIP_WRITE_BARRIER);
set_min(nan, SKIP_WRITE_BARRIER);
set_sec(nan, SKIP_WRITE_BARRIER);
set_weekday(nan, SKIP_WRITE_BARRIER);
} else {
set_cache_stamp(Smi::FromInt(DateCache::kInvalidStamp), SKIP_WRITE_BARRIER);
}
}
void JSDate::SetLocalFields(int64_t local_time_ms, DateCache* date_cache) {
int days = DateCache::DaysFromTime(local_time_ms);
int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days);
int year, month, day;
date_cache->YearMonthDayFromDays(days, &year, &month, &day);
int weekday = date_cache->Weekday(days);
int hour = time_in_day_ms / (60 * 60 * 1000);
int min = (time_in_day_ms / (60 * 1000)) % 60;
int sec = (time_in_day_ms / 1000) % 60;
set_cache_stamp(date_cache->stamp());
set_year(Smi::FromInt(year), SKIP_WRITE_BARRIER);
set_month(Smi::FromInt(month), SKIP_WRITE_BARRIER);
set_day(Smi::FromInt(day), SKIP_WRITE_BARRIER);
set_weekday(Smi::FromInt(weekday), SKIP_WRITE_BARRIER);
set_hour(Smi::FromInt(hour), SKIP_WRITE_BARRIER);
set_min(Smi::FromInt(min), SKIP_WRITE_BARRIER);
set_sec(Smi::FromInt(sec), SKIP_WRITE_BARRIER);
}
} } // namespace v8::internal

View File

@ -6028,32 +6028,42 @@ class JSValue: public JSObject {
};
class DateCache;
// Representation for JS date objects.
class JSDate: public JSObject {
public:
// If one component is NaN, all of them are, indicating a NaN time value.
// [value]: the time value.
DECL_ACCESSORS(value, Object)
// [local]: the offset for the local time value.
DECL_ACCESSORS(local, Object)
// [year]: caches year. Either undefined, smi, or NaN.
DECL_ACCESSORS(year, Object)
// [month]: caches month. Either undefined, smi, or NaN.
DECL_ACCESSORS(month, Object)
// [day]: caches day. Either undefined, smi, or NaN.
DECL_ACCESSORS(day, Object)
// [weekday]: caches day of week. Either undefined, smi, or NaN.
DECL_ACCESSORS(weekday, Object)
// [hour]: caches hours. Either undefined, smi, or NaN.
DECL_ACCESSORS(hour, Object)
// [min]: caches minutes. Either undefined, smi, or NaN.
DECL_ACCESSORS(min, Object)
// [sec]: caches seconds. Either undefined, smi, or NaN.
DECL_ACCESSORS(sec, Object)
// [weekday]: caches day of week. Either undefined, smi, or NaN.
DECL_ACCESSORS(weekday, Object)
// [cache stamp]: sample of the date cache stamp at the
// moment when local fields were cached.
DECL_ACCESSORS(cache_stamp, Object)
// Casting.
static inline JSDate* cast(Object* obj);
// Returns the date field with the specified index.
// See FieldIndex for the list of date fields.
static MaybeObject* GetField(Object* date, Smi* index);
void SetValue(Object* value, bool is_value_nan);
// Dispatched behavior.
#ifdef OBJECT_PRINT
inline void JSDatePrint() {
@ -6064,23 +6074,56 @@ class JSDate: public JSObject {
#ifdef DEBUG
void JSDateVerify();
#endif
// The order is important. It must be kept in sync with date macros
// in macros.py.
enum FieldIndex {
kDateValue,
kYear,
kMonth,
kDay,
kWeekday,
kHour,
kMinute,
kSecond,
kFirstUncachedField,
kMillisecond = kFirstUncachedField,
kDays,
kTimeInDay,
kFirstUTCField,
kYearUTC = kFirstUTCField,
kMonthUTC,
kDayUTC,
kWeekdayUTC,
kHourUTC,
kMinuteUTC,
kSecondUTC,
kMillisecondUTC,
kDaysUTC,
kTimeInDayUTC,
kTimezoneOffset
};
// Layout description.
static const int kValueOffset = JSObject::kHeaderSize;
static const int kLocalOffset = kValueOffset + kPointerSize;
static const int kYearOffset = kLocalOffset + kPointerSize;
static const int kYearOffset = kValueOffset + kPointerSize;
static const int kMonthOffset = kYearOffset + kPointerSize;
static const int kDayOffset = kMonthOffset + kPointerSize;
static const int kHourOffset = kDayOffset + kPointerSize;
static const int kWeekdayOffset = kDayOffset + kPointerSize;
static const int kHourOffset = kWeekdayOffset + kPointerSize;
static const int kMinOffset = kHourOffset + kPointerSize;
static const int kSecOffset = kMinOffset + kPointerSize;
static const int kWeekdayOffset = kSecOffset + kPointerSize;
static const int kSize = kWeekdayOffset + kPointerSize;
// Index of first field not requiring a write barrier.
static const int kFirstBarrierFree = 2; // year
static const int kCacheStampOffset = kSecOffset + kPointerSize;
static const int kSize = kCacheStampOffset + kPointerSize;
private:
inline Object* DoGetField(FieldIndex index);
Object* GetUTCField(FieldIndex index, double value, DateCache* date_cache);
// Computes and caches the cacheable fields of the date.
inline void SetLocalFields(int64_t local_time_ms, DateCache* date_cache);
DISALLOW_IMPLICIT_CONSTRUCTORS(JSDate);
};

View File

@ -40,6 +40,7 @@
#include "dateparser-inl.h"
#include "debug.h"
#include "deoptimizer.h"
#include "date.h"
#include "execution.h"
#include "global-handles.h"
#include "isolate-inl.h"
@ -7542,51 +7543,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_tan) {
}
static int MakeDay(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];
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_DateMakeDay) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@ -7594,319 +7550,39 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DateMakeDay) {
CONVERT_SMI_ARG_CHECKED(year, 0);
CONVERT_SMI_ARG_CHECKED(month, 1);
return Smi::FromInt(MakeDay(year, month));
return Smi::FromInt(isolate->date_cache()->DaysFromYearMonth(year, month));
}
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;
RUNTIME_FUNCTION(MaybeObject*, Runtime_DateSetValue) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
static const char kDayInYear[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
CONVERT_ARG_HANDLE_CHECKED(JSDate, date, 0);
CONVERT_DOUBLE_ARG_CHECKED(time, 1);
CONVERT_SMI_ARG_CHECKED(is_utc, 2);
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
DateCache* date_cache = isolate->date_cache();
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
static const char kMonthInYear[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
// This function works for dates from 1970 to 2099.
static inline void DateYMDFromTimeAfter1970(int date,
int& year, int& month, int& day) {
#ifdef DEBUG
int save_date = date; // Need this for ASSERT in the end.
#endif
year = 1970 + (4 * date + 2) / kDaysIn4Years;
date %= kDaysIn4Years;
month = kMonthInYear[date];
day = kDayInYear[date];
ASSERT(MakeDay(year, month) + day - 1 == save_date);
}
static inline void DateYMDFromTimeSlow(int date,
int& year, int& month, int& day) {
#ifdef DEBUG
int save_date = date; // Need this for ASSERT in the end.
#endif
date += kDaysOffset;
year = 400 * (date / kDaysIn400Years) - kYearsOffset;
date %= kDaysIn400Years;
ASSERT(MakeDay(year, 0) + date == save_date);
date--;
int yd1 = date / kDaysIn100Years;
date %= kDaysIn100Years;
year += 100 * yd1;
date++;
int yd2 = date / kDaysIn4Years;
date %= kDaysIn4Years;
year += 4 * yd2;
date--;
int yd3 = date / 365;
date %= 365;
year += yd3;
bool is_leap = (!yd1 || yd2) && !yd3;
ASSERT(date >= -1);
ASSERT(is_leap || (date >= 0));
ASSERT((date < 365) || (is_leap && (date < 366)));
ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
ASSERT(is_leap || ((MakeDay(year, 0) + date) == save_date));
ASSERT(!is_leap || ((MakeDay(year, 0) + date + 1) == save_date));
if (is_leap) {
day = kDayInYear[2*365 + 1 + date];
month = kMonthInYear[2*365 + 1 + date];
Object* value = NULL;
bool is_value_nan = false;
if (isnan(time)) {
value = isolate->heap()->nan_value();
is_value_nan = true;
} else {
day = kDayInYear[date];
month = kMonthInYear[date];
time = is_utc ? time : date_cache->ToUTC(time);
if (time < -DateCache::kMaxTimeInMs ||
time > DateCache::kMaxTimeInMs) {
value = isolate->heap()->nan_value();
is_value_nan = true;
} else {
MaybeObject* maybe_result =
isolate->heap()->AllocateHeapNumber(DoubleToInteger(time));
if (!maybe_result->ToObject(&value)) return maybe_result;
}
}
ASSERT(MakeDay(year, month) + day - 1 == save_date);
}
static inline void DateYMDFromTime(int date,
int& year, int& month, int& day) {
if (date >= 0 && date < 32 * kDaysIn4Years) {
DateYMDFromTimeAfter1970(date, year, month, day);
} else {
DateYMDFromTimeSlow(date, year, month, day);
}
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_DateYMDFromTime) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
CONVERT_DOUBLE_ARG_CHECKED(t, 0);
CONVERT_ARG_CHECKED(JSArray, res_array, 1);
int year, month, day;
DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
FixedArrayBase* elms_base = FixedArrayBase::cast(res_array->elements());
RUNTIME_ASSERT(elms_base->length() == 3);
RUNTIME_ASSERT(res_array->HasFastTypeElements());
MaybeObject* maybe = res_array->EnsureWritableFastElements();
if (maybe->IsFailure()) return maybe;
FixedArray* elms = FixedArray::cast(res_array->elements());
elms->set(0, Smi::FromInt(year));
elms->set(1, Smi::FromInt(month));
elms->set(2, Smi::FromInt(day));
return isolate->heap()->undefined_value();
date->SetValue(value, is_value_nan);
return *date;
}
@ -9382,25 +9058,20 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DateLocalTimezone) {
ASSERT(args.length() == 1);
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
const char* zone = OS::LocalTimezone(x);
int64_t time = isolate->date_cache()->EquivalentTime(static_cast<int64_t>(x));
const char* zone = OS::LocalTimezone(time);
return isolate->heap()->AllocateStringFromUtf8(CStrVector(zone));
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_DateLocalTimeOffset) {
NoHandleAllocation ha;
ASSERT(args.length() == 0);
return isolate->heap()->NumberFromDouble(OS::LocalTimeOffset());
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_DateDaylightSavingsOffset) {
RUNTIME_FUNCTION(MaybeObject*, Runtime_DateToUTC) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
CONVERT_DOUBLE_ARG_CHECKED(x, 0);
return isolate->heap()->NumberFromDouble(OS::DaylightSavingsOffset(x));
int64_t time = isolate->date_cache()->ToUTC(static_cast<int64_t>(x));
return isolate->heap()->NumberFromDouble(time);
}

View File

@ -244,10 +244,9 @@ namespace internal {
F(DateCurrentTime, 0, 1) \
F(DateParseString, 2, 1) \
F(DateLocalTimezone, 1, 1) \
F(DateLocalTimeOffset, 0, 1) \
F(DateDaylightSavingsOffset, 1, 1) \
F(DateToUTC, 1, 1) \
F(DateMakeDay, 2, 1) \
F(DateYMDFromTime, 2, 1) \
F(DateSetValue, 3, 1) \
\
/* Numbers */ \
\
@ -491,8 +490,7 @@ namespace internal {
F(Arguments, 1, 1) \
F(ValueOf, 1, 1) \
F(SetValueOf, 2, 1) \
F(DateField, 2, 1) \
F(SetDateField, 3, 1) \
F(DateField, 2 /* date object, field index */, 1) \
F(StringCharFromCode, 1, 1) \
F(StringCharAt, 2, 1) \
F(ObjectEquals, 2, 1) \

View File

@ -502,6 +502,14 @@ void ExternalReferenceTable::PopulateTable(Isolate* isolate) {
UNCLASSIFIED,
45,
"the_hole_nan");
Add(ExternalReference::get_date_field_function(isolate).address(),
UNCLASSIFIED,
46,
"JSDate::GetField");
Add(ExternalReference::date_cache_stamp(isolate).address(),
UNCLASSIFIED,
47,
"date_cache_stamp");
}

View File

@ -2838,17 +2838,46 @@ void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 2);
ASSERT_NE(NULL, args->at(1)->AsLiteral());
int index = Smi::cast(*(args->at(1)->AsLiteral()->handle()))->value();
Smi* index = Smi::cast(*(args->at(1)->AsLiteral()->handle()));
VisitForAccumulatorValue(args->at(0)); // Load the object.
Label runtime, done;
Register object = rax;
Register result = rax;
Register scratch = rcx;
#ifdef DEBUG
__ AbortIfSmi(rax);
__ CmpObjectType(rax, JS_DATE_TYPE, rbx);
__ AbortIfSmi(object);
__ CmpObjectType(object, JS_DATE_TYPE, scratch);
__ Assert(equal, "Trying to get date field from non-date.");
#endif
__ movq(rax, FieldOperand(rax, JSDate::kValueOffset + kPointerSize * index));
if (index->value() == 0) {
__ movq(result, FieldOperand(object, JSDate::kValueOffset));
} else {
if (index->value() < JSDate::kFirstUncachedField) {
ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
__ movq(scratch, stamp);
__ cmpq(scratch, FieldOperand(object, JSDate::kCacheStampOffset));
__ j(not_equal, &runtime, Label::kNear);
__ movq(result, FieldOperand(object, JSDate::kValueOffset +
kPointerSize * index->value()));
__ jmp(&done);
}
__ bind(&runtime);
__ PrepareCallCFunction(2);
#ifdef _WIN64
__ movq(rcx, object);
__ movq(rdx, index, RelocInfo::NONE);
#else
__ movq(rdi, object);
__ movq(rsi, index, RelocInfo::NONE);
#endif
__ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
__ bind(&done);
}
context()->Plug(rax);
}
@ -2893,36 +2922,6 @@ void FullCodeGenerator::EmitSetValueOf(CallRuntime* expr) {
}
void FullCodeGenerator::EmitSetDateField(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT(args->length() == 3);
ASSERT_NE(NULL, args->at(1)->AsLiteral());
int index = Smi::cast(*(args->at(1)->AsLiteral()->handle()))->value();
VisitForStackValue(args->at(0)); // Load the object.
VisitForAccumulatorValue(args->at(2)); // Load the value.
__ pop(rbx); // rax = value. rbx = object.
#ifdef DEBUG
__ AbortIfSmi(rbx);
__ CmpObjectType(rbx, JS_DATE_TYPE, rcx);
__ Assert(equal, "Trying to set date field on non-date.");
#endif
// Store the value.
__ movq(FieldOperand(rbx, JSDate::kValueOffset + kPointerSize * index), rax);
// Caches can only be smi or NaN, so we can skip the write barrier for them.
if (index < JSDate::kFirstBarrierFree) {
// Update the write barrier. Save the value as it will be
// overwritten by the write barrier code and is needed afterward.
__ movq(rdx, rax);
__ RecordWriteField(rbx, JSDate::kValueOffset + kPointerSize * index,
rdx, rcx, kDontSaveFPRegs);
}
context()->Plug(rax);
}
void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT_EQ(args->length(), 1);

View File

@ -1225,42 +1225,44 @@ void LCodeGen::DoValueOf(LValueOf* instr) {
void LCodeGen::DoDateField(LDateField* instr) {
Register input = ToRegister(instr->InputAt(0));
Register object = ToRegister(instr->InputAt(0));
Register result = ToRegister(instr->result());
ASSERT(input.is(result));
Smi* index = instr->index();
Label runtime, done;
ASSERT(object.is(result));
ASSERT(object.is(rax));
#ifdef DEBUG
__ AbortIfSmi(input);
__ CmpObjectType(input, JS_DATE_TYPE, kScratchRegister);
__ AbortIfSmi(object);
__ CmpObjectType(object, JS_DATE_TYPE, kScratchRegister);
__ Assert(equal, "Trying to get date field from non-date.");
#endif
__ movq(result, FieldOperand(input,
JSDate::kValueOffset + kPointerSize * instr->index()));
}
void LCodeGen::DoSetDateField(LSetDateField* instr) {
Register date = ToRegister(instr->InputAt(0));
Register value = ToRegister(instr->InputAt(1));
Register result = ToRegister(instr->result());
int index = instr->index();
#ifdef DEBUG
__ AbortIfSmi(date);
__ CmpObjectType(date, JS_DATE_TYPE, kScratchRegister);
__ Assert(equal, "Trying to get date field from non-date.");
if (index->value() == 0) {
__ movq(result, FieldOperand(object, JSDate::kValueOffset));
} else {
if (index->value() < JSDate::kFirstUncachedField) {
ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
__ movq(kScratchRegister, stamp);
__ cmpq(kScratchRegister, FieldOperand(object,
JSDate::kCacheStampOffset));
__ j(not_equal, &runtime, Label::kNear);
__ movq(result, FieldOperand(object, JSDate::kValueOffset +
kPointerSize * index->value()));
__ jmp(&done);
}
__ bind(&runtime);
__ PrepareCallCFunction(2);
#ifdef _WIN64
__ movq(rcx, object);
__ movq(rdx, index, RelocInfo::NONE);
#else
__ movq(rdi, object);
__ movq(rsi, index, RelocInfo::NONE);
#endif
__ movq(FieldOperand(date, JSDate::kValueOffset + kPointerSize * index),
value);
// Caches can only be smi or NaN, so we can skip the write barrier for them.
if (index < JSDate::kFirstBarrierFree) {
// Update the write barrier. Save the value as it will be
// overwritten by the write barrier code and is needed afterward.
__ movq(result, value);
__ RecordWriteField(date, JSDate::kValueOffset + kPointerSize * index,
value, kScratchRegister, kDontSaveFPRegs);
__ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
__ bind(&done);
}
}

View File

@ -1602,17 +1602,9 @@ LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
LOperand* date = UseRegister(instr->value());
LDateField* result = new LDateField(date, instr->index());
return DefineSameAsFirst(result);
}
LInstruction* LChunkBuilder::DoSetDateField(HSetDateField* instr) {
LOperand* date = UseTempRegister(instr->OperandAt(1));
LOperand* value = UseTempRegister(instr->OperandAt(2));
LSetDateField* result = new LSetDateField(date, value, instr->index());
return DefineAsRegister(result);
LOperand* object = UseFixed(instr->value(), rax);
LDateField* result = new LDateField(object, instr->index());
return MarkAsCall(DefineFixed(result, rax), instr);
}

View File

@ -178,8 +178,7 @@ class LCodeGen;
V(ForInCacheArray) \
V(CheckMapValue) \
V(LoadFieldByIndex) \
V(DateField) \
V(SetDateField)
V(DateField)
#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
@ -991,34 +990,17 @@ class LValueOf: public LTemplateInstruction<1, 1, 0> {
class LDateField: public LTemplateInstruction<1, 1, 0> {
public:
LDateField(LOperand* date, int index) : index_(index) {
LDateField(LOperand* date, Smi* index) : index_(index) {
inputs_[0] = date;
}
DECLARE_CONCRETE_INSTRUCTION(ValueOf, "date-field")
DECLARE_HYDROGEN_ACCESSOR(ValueOf)
int index() const { return index_; }
Smi* index() const { return index_; }
private:
int index_;
};
class LSetDateField: public LTemplateInstruction<1, 2, 0> {
public:
LSetDateField(LOperand* date, LOperand* value, int index) : index_(index) {
inputs_[0] = date;
inputs_[1] = value;
}
DECLARE_CONCRETE_INSTRUCTION(DateField, "date-set-field")
DECLARE_HYDROGEN_ACCESSOR(DateField)
int index() const { return index_; }
private:
int index_;
Smi* index_;
};

View File

@ -62,6 +62,7 @@ SOURCES = {
'test-conversions.cc',
'test-cpu-profiler.cc',
'test-dataflow.cc',
'test-date.cc',
'test-debug.cc',
'test-decls.cc',
'test-deoptimization.cc',

View File

@ -57,6 +57,7 @@
'test-conversions.cc',
'test-cpu-profiler.cc',
'test-dataflow.cc',
'test-date.cc',
'test-debug.cc',
'test-decls.cc',
'test-deoptimization.cc',

168
test/cctest/test-date.cc Normal file
View File

@ -0,0 +1,168 @@
// 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 "global-handles.h"
#include "snapshot.h"
#include "cctest.h"
using namespace v8::internal;
class DateCacheMock: public DateCache {
public:
struct Rule {
int year, start_month, start_day, end_month, end_day, offset_sec;
};
DateCacheMock(int local_offset, Rule* rules, int rules_count)
: local_offset_(local_offset), rules_(rules), rules_count_(rules_count) {}
protected:
virtual int GetDaylightSavingsOffsetFromOS(int64_t time_sec) {
int days = DaysFromTime(time_sec * 1000);
int time_in_day_sec = TimeInDay(time_sec * 1000, days) / 1000;
int year, month, day;
YearMonthDayFromDays(days, &year, &month, &day);
Rule* rule = FindRuleFor(year, month, day, time_in_day_sec);
return rule == NULL ? 0 : rule->offset_sec * 1000;
}
virtual int GetLocalOffsetFromOS() {
return local_offset_;
}
private:
Rule* FindRuleFor(int year, int month, int day, int time_in_day_sec) {
Rule* result = NULL;
for (int i = 0; i < rules_count_; i++)
if (Match(&rules_[i], year, month, day, time_in_day_sec)) {
result = &rules_[i];
}
return result;
}
bool Match(Rule* rule, int year, int month, int day, int time_in_day_sec) {
if (rule->year != 0 && rule->year != year) return false;
if (rule->start_month > month) return false;
if (rule->end_month < month) return false;
int start_day = ComputeRuleDay(year, rule->start_month, rule->start_day);
if (rule->start_month == month && start_day > day) return false;
if (rule->start_month == month && start_day == day &&
2 * 3600 > time_in_day_sec)
return false;
int end_day = ComputeRuleDay(year, rule->end_month, rule->end_day);
if (rule->end_month == month && end_day < day) return false;
if (rule->end_month == month && end_day == day &&
2 * 3600 <= time_in_day_sec)
return false;
return true;
}
int ComputeRuleDay(int year, int month, int day) {
if (day != 0) return day;
int days = DaysFromYearMonth(year, month);
// Find the first Sunday of the month.
while (Weekday(days + day) != 6) day++;
return day + 1;
}
int local_offset_;
Rule* rules_;
int rules_count_;
};
static int64_t TimeFromYearMonthDay(DateCache* date_cache,
int year,
int month,
int day) {
int64_t result = date_cache->DaysFromYearMonth(year, month);
return (result + day - 1) * DateCache::kMsPerDay;
}
static void CheckDST(int64_t time) {
Isolate* isolate = Isolate::Current();
DateCache* date_cache = isolate->date_cache();
int64_t actual = date_cache->ToLocal(time);
int64_t expected = time + date_cache->GetLocalOffsetFromOS() +
date_cache->GetDaylightSavingsOffsetFromOS(time / 1000);
CHECK_EQ(actual, expected);
}
TEST(DaylightSavingsTime) {
LocalContext context;
v8::HandleScope scope;
Isolate* isolate = Isolate::Current();
DateCacheMock::Rule rules[] = {
{0, 2, 0, 10, 0, 3600}, // DST from March to November in any year.
{2010, 2, 0, 7, 20, 3600}, // DST from March to August 20 in 2010.
{2010, 7, 20, 8, 10, 0}, // No DST from August 20 to September 10 in 2010.
{2010, 8, 10, 10, 0, 3600}, // DST from September 10 to November in 2010.
};
int local_offset_ms = -36000000; // -10 hours.
DateCacheMock* date_cache =
new DateCacheMock(local_offset_ms, rules, ARRAY_SIZE(rules));
isolate->set_date_cache(date_cache);
int64_t start_of_2010 = TimeFromYearMonthDay(date_cache, 2010, 0, 1);
int64_t start_of_2011 = TimeFromYearMonthDay(date_cache, 2011, 0, 1);
int64_t august_20 = TimeFromYearMonthDay(date_cache, 2010, 7, 20);
int64_t september_10 = TimeFromYearMonthDay(date_cache, 2010, 8, 10);
CheckDST((august_20 + september_10) / 2);
CheckDST(september_10);
CheckDST(september_10 + 2 * 3600);
CheckDST(september_10 + 2 * 3600 - 1000);
CheckDST(august_20 + 2 * 3600);
CheckDST(august_20 + 2 * 3600 - 1000);
CheckDST(august_20);
// Check each day of 2010.
for (int64_t time = start_of_2011 + 2 * 3600;
time >= start_of_2010;
time -= DateCache::kMsPerDay) {
CheckDST(time);
CheckDST(time - 1000);
CheckDST(time + 1000);
}
// Check one day from 2010 to 2100.
for (int year = 2100; year >= 2010; year--) {
CheckDST(TimeFromYearMonthDay(date_cache, year, 5, 5));
}
CheckDST((august_20 + september_10) / 2);
CheckDST(september_10);
CheckDST(september_10 + 2 * 3600);
CheckDST(september_10 + 2 * 3600 - 1000);
CheckDST(august_20 + 2 * 3600);
CheckDST(august_20 + 2 * 3600 - 1000);
CheckDST(august_20);
}

View File

@ -187,7 +187,6 @@ var knownProblems = {
// This functions perform some checks compile time (they require one of their
// arguments to be a compile time smi).
"_DateField": true,
"_SetDateField": true,
"_GetFromCache": true,
// This function expects its first argument to be a non-smi.

View File

@ -282,6 +282,8 @@
'../../src/cpu-profiler.h',
'../../src/data-flow.cc',
'../../src/data-flow.h',
'../../src/date.cc',
'../../src/date.h',
'../../src/dateparser.cc',
'../../src/dateparser.h',
'../../src/dateparser-inl.h',