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:
parent
13d31b6594
commit
1767fef60b
@ -59,6 +59,7 @@ SOURCES = {
|
||||
counters.cc
|
||||
cpu-profiler.cc
|
||||
data-flow.cc
|
||||
date.cc
|
||||
dateparser.cc
|
||||
debug-agent.cc
|
||||
debug.cc
|
||||
|
@ -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 =
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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
384
src/date.cc
Normal 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
254
src/date.h
Normal 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
|
703
src/date.js
703
src/date.js
File diff suppressed because it is too large
Load Diff
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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]);
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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) {
|
||||
|
130
src/objects.cc
130
src/objects.cc
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
393
src/runtime.cc
393
src/runtime.cc
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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
168
test/cctest/test-date.cc
Normal 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);
|
||||
}
|
@ -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.
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user