Version 3.9.17
Fixed VFP detection through compiler defines. (issue 1996)
Add Code-related fields to postmortem metadata.
Performance and stability improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@11000 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index 7901bc4..9ba1362 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2012-03-12: Version 3.9.17
+
+ Fixed VFP detection through compiler defines. (issue 1996)
+
+ Add Code-related fields to postmortem metadata.
+
+ Performance and stability improvements on all platforms.
+
+
2012-03-09: Version 3.9.16
Added basic interface inference for modules (behind the --harmony flag).
diff --git a/include/v8.h b/include/v8.h
index 893df12..e4037b9 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -3871,7 +3871,7 @@
static const int kFullStringRepresentationMask = 0x07;
static const int kExternalTwoByteRepresentationTag = 0x02;
- static const int kJSObjectType = 0xa9;
+ static const int kJSObjectType = 0xaa;
static const int kFirstNonstringType = 0x80;
static const int kForeignType = 0x85;
diff --git a/src/SConscript b/src/SConscript
index 40b2c54..fde7a80 100755
--- a/src/SConscript
+++ b/src/SConscript
@@ -59,6 +59,7 @@
counters.cc
cpu-profiler.cc
data-flow.cc
+ date.cc
dateparser.cc
debug-agent.cc
debug.cc
diff --git a/src/allocation.h b/src/allocation.h
index 69e72bd..31067dd 100644
--- a/src/allocation.h
+++ b/src/allocation.h
@@ -80,7 +80,7 @@
template <typename T>
-T* NewArray(int size) {
+T* NewArray(size_t size) {
T* result = new T[size];
if (result == NULL) Malloced::FatalProcessOutOfMemory();
return result;
diff --git a/src/api.cc b/src/api.cc
index ede6018..d8c7ba0 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -4735,8 +4735,8 @@
if (IsDeadCheck(isolate, "v8::Date::NumberValue()")) return 0;
LOG_API(isolate, "Date::NumberValue");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
- i::Handle<i::JSValue> jsvalue = i::Handle<i::JSValue>::cast(obj);
- return jsvalue->value()->Number();
+ i::Handle<i::JSDate> jsdate = i::Handle<i::JSDate>::cast(obj);
+ return jsdate->value()->Number();
}
@@ -4747,8 +4747,10 @@
LOG_API(isolate, "Date::DateTimeConfigurationChangeNotification");
ENTER_V8(isolate);
+ isolate->date_cache()->ResetDateCache();
+
i::HandleScope scope(isolate);
- // Get the function ResetDateCache (defined in date-delay.js).
+ // Get the function ResetDateCache (defined in date.js).
i::Handle<i::String> func_name_str =
isolate->factory()->LookupAsciiSymbol("ResetDateCache");
i::MaybeObject* result =
@@ -5979,7 +5981,7 @@
i::Isolate* isolate = i::Isolate::Current();
IsDeadCheck(isolate, "v8::HeapSnapshot::GetNodeById");
return reinterpret_cast<const HeapGraphNode*>(
- ToInternal(this)->GetEntryById(id));
+ ToInternal(this)->GetEntryById(static_cast<i::SnapshotObjectId>(id)));
}
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc
index 2592236..ff15221 100644
--- a/src/arm/assembler-arm.cc
+++ b/src/arm/assembler-arm.cc
@@ -66,11 +66,13 @@
#ifdef __arm__
// If the compiler is allowed to use VFP then we can use VFP too in our code
- // generation even when generating snapshots. This won't work for cross
- // compilation. VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
-#if defined(__VFP_FP__) && !defined(__SOFTFP__)
+ // generation even when generating snapshots. ARMv7 and hardware floating
+ // point support implies VFPv3, see ARM DDI 0406B, page A1-6.
+#if defined(CAN_USE_ARMV7_INSTRUCTIONS) && defined(__VFP_FP__) \
+ && !defined(__SOFTFP__)
answer |= 1u << VFP3 | 1u << ARMv7;
-#endif // defined(__VFP_FP__) && !defined(__SOFTFP__)
+#endif // defined(CAN_USE_ARMV7_INSTRUCTIONS) && defined(__VFP_FP__)
+ // && !defined(__SOFTFP__)
#endif // def __arm__
return answer;
diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc
index 3a4ce74..250f020 100644
--- a/src/arm/code-stubs-arm.cc
+++ b/src/arm/code-stubs-arm.cc
@@ -6661,6 +6661,8 @@
ASSERT(state_ == CompareIC::STRINGS);
Label miss;
+ bool equality = Token::IsEqualityOp(op_);
+
// Registers containing left and right operands respectively.
Register left = r1;
Register right = r0;
@@ -6694,28 +6696,39 @@
// Check that both strings are symbols. If they are, we're done
// because we already know they are not identical.
- ASSERT(GetCondition() == eq);
- STATIC_ASSERT(kSymbolTag != 0);
- __ and_(tmp3, tmp1, Operand(tmp2));
- __ tst(tmp3, Operand(kIsSymbolMask));
- // Make sure r0 is non-zero. At this point input operands are
- // guaranteed to be non-zero.
- ASSERT(right.is(r0));
- __ Ret(ne);
+ if (equality) {
+ ASSERT(GetCondition() == eq);
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ and_(tmp3, tmp1, Operand(tmp2));
+ __ tst(tmp3, Operand(kIsSymbolMask));
+ // Make sure r0 is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(r0));
+ __ Ret(ne);
+ }
// Check that both strings are sequential ASCII.
Label runtime;
- __ JumpIfBothInstanceTypesAreNotSequentialAscii(tmp1, tmp2, tmp3, tmp4,
- &runtime);
+ __ JumpIfBothInstanceTypesAreNotSequentialAscii(
+ tmp1, tmp2, tmp3, tmp4, &runtime);
// Compare flat ASCII strings. Returns when done.
- StringCompareStub::GenerateFlatAsciiStringEquals(
- masm, left, right, tmp1, tmp2, tmp3);
+ if (equality) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(
+ masm, left, right, tmp1, tmp2, tmp3);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(
+ masm, left, right, tmp1, tmp2, tmp3, tmp4);
+ }
// Handle more complex cases in runtime.
__ bind(&runtime);
__ Push(left, right);
- __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+ if (equality) {
+ __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+ } else {
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+ }
__ bind(&miss);
GenerateMiss(masm);
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index 303f0b0..77f4e44 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -2947,6 +2947,50 @@
}
+void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+ ASSERT_NE(NULL, args->at(1)->AsLiteral());
+ 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(object);
+ __ CompareObjectType(object, scratch1, scratch1, JS_DATE_TYPE);
+ __ Assert(eq, "Trying to get date field from non-date.");
+#endif
+
+ 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);
+}
+
+
void FullCodeGenerator::EmitMathPow(CallRuntime* expr) {
// Load the arguments on the stack and call the runtime function.
ZoneList<Expression*>* args = expr->arguments();
diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc
index 108a6de..36421d9 100644
--- a/src/arm/lithium-arm.cc
+++ b/src/arm/lithium-arm.cc
@@ -1602,6 +1602,13 @@
}
+LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
+ LOperand* object = UseFixed(instr->value(), r0);
+ LDateField* result = new LDateField(object, FixedTemp(r1), instr->index());
+ return MarkAsCall(DefineFixed(result, r0), instr);
+}
+
+
LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) {
LOperand* value = UseRegisterAtStart(instr->index());
LOperand* length = UseRegister(instr->length());
diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h
index 35ed67d..ae19677 100644
--- a/src/arm/lithium-arm.h
+++ b/src/arm/lithium-arm.h
@@ -177,8 +177,8 @@
V(ForInPrepareMap) \
V(ForInCacheArray) \
V(CheckMapValue) \
- V(LoadFieldByIndex)
-
+ V(LoadFieldByIndex) \
+ V(DateField)
#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
@@ -990,6 +990,41 @@
};
+class LDateField: public LTemplateInstruction<1, 1, 1> {
+ public:
+ 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)
+ Smi* index() const { return index_; }
+
+ private:
+ Smi* 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_;
+};
+
+
class LThrow: public LTemplateInstruction<0, 1, 0> {
public:
explicit LThrow(LOperand* value) {
diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc
index 2a707a8..012ea45 100644
--- a/src/arm/lithium-codegen-arm.cc
+++ b/src/arm/lithium-codegen-arm.cc
@@ -1438,6 +1438,46 @@
}
+void LCodeGen::DoDateField(LDateField* instr) {
+ Register object = ToRegister(instr->InputAt(0));
+ Register result = ToRegister(instr->result());
+ 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(object);
+ __ CompareObjectType(object, scratch, scratch, JS_DATE_TYPE);
+ __ Assert(eq, "Trying to get date field from non-date.");
+#endif
+
+ 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);
+ }
+}
+
+
void LCodeGen::DoBitNotI(LBitNotI* instr) {
Register input = ToRegister(instr->InputAt(0));
Register result = ToRegister(instr->result());
diff --git a/src/assembler.cc b/src/assembler.cc
index 0bec577..07509b5 100644
--- a/src/assembler.cc
+++ b/src/assembler.cc
@@ -813,6 +813,17 @@
}
+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(
diff --git a/src/assembler.h b/src/assembler.h
index e7c92b4..5063879 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -595,6 +595,9 @@
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);
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 19a6a15..a48bb4d 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -927,7 +927,7 @@
{ // --- D a t e ---
// Builtin functions for Date.prototype.
Handle<JSFunction> date_fun =
- InstallFunction(global, "Date", JS_VALUE_TYPE, JSValue::kSize,
+ InstallFunction(global, "Date", JS_DATE_TYPE, JSDate::kSize,
isolate->initial_object_prototype(),
Builtins::kIllegal, true);
diff --git a/src/builtins.cc b/src/builtins.cc
index 7290a2c..ca202f2 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -310,28 +310,6 @@
}
-static void CopyElements(Heap* heap,
- AssertNoAllocation* no_gc,
- FixedArray* dst,
- int dst_index,
- FixedArray* src,
- int src_index,
- int len) {
- if (len == 0) return;
- ASSERT(dst != src); // Use MoveElements instead.
- ASSERT(dst->map() != HEAP->fixed_cow_array_map());
- ASSERT(len > 0);
- CopyWords(dst->data_start() + dst_index,
- src->data_start() + src_index,
- len);
- WriteBarrierMode mode = dst->GetWriteBarrierMode(*no_gc);
- if (mode == UPDATE_WRITE_BARRIER) {
- heap->RecordWrites(dst->address(), dst->OffsetOfElementAt(dst_index), len);
- }
- heap->incremental_marking()->RecordWrites(dst);
-}
-
-
static void MoveElements(Heap* heap,
AssertNoAllocation* no_gc,
FixedArray* dst,
@@ -531,7 +509,8 @@
FixedArray* new_elms = FixedArray::cast(obj);
AssertNoAllocation no_gc;
- CopyElements(heap, &no_gc, new_elms, 0, elms, 0, len);
+ CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS, 0,
+ new_elms, FAST_ELEMENTS, 0, len);
FillWithHoles(heap, new_elms, new_length, capacity);
elms = new_elms;
@@ -667,7 +646,8 @@
}
FixedArray* new_elms = FixedArray::cast(obj);
AssertNoAllocation no_gc;
- CopyElements(heap, &no_gc, new_elms, to_add, elms, 0, len);
+ CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS, 0,
+ new_elms, FAST_ELEMENTS, to_add, len);
FillWithHoles(heap, new_elms, new_length, capacity);
elms = new_elms;
array->set_elements(elms);
@@ -778,8 +758,9 @@
if (!maybe_array->To(&result_array)) return maybe_array;
AssertNoAllocation no_gc;
- CopyElements(heap, &no_gc, FixedArray::cast(result_array->elements()), 0,
- elms, k, result_len);
+ CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS, k,
+ FixedArray::cast(result_array->elements()),
+ FAST_ELEMENTS, 0, result_len);
return result_array;
}
@@ -852,11 +833,9 @@
{
AssertNoAllocation no_gc;
// Fill newly created array.
- CopyElements(heap,
- &no_gc,
- FixedArray::cast(result_array->elements()), 0,
- elms, actual_start,
- actual_delete_count);
+ CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS, actual_start,
+ FixedArray::cast(result_array->elements()),
+ FAST_ELEMENTS, 0, actual_delete_count);
}
int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0;
@@ -906,12 +885,13 @@
{
AssertNoAllocation no_gc;
// Copy the part before actual_start as is.
- CopyElements(heap, &no_gc, new_elms, 0, elms, 0, actual_start);
+ CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS, 0,
+ new_elms, FAST_ELEMENTS, 0, actual_start);
const int to_copy = len - actual_delete_count - actual_start;
- CopyElements(heap, &no_gc,
- new_elms, actual_start + item_count,
- elms, actual_start + actual_delete_count,
- to_copy);
+ CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS,
+ actual_start + actual_delete_count,
+ new_elms, FAST_ELEMENTS,
+ actual_start + item_count, to_copy);
}
FillWithHoles(heap, new_elms, new_length, capacity);
@@ -1000,7 +980,9 @@
JSArray* array = JSArray::cast(args[i]);
int len = Smi::cast(array->length())->value();
FixedArray* elms = FixedArray::cast(array->elements());
- CopyElements(heap, &no_gc, result_elms, start_pos, elms, 0, len);
+ CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS, 0,
+ result_elms, FAST_ELEMENTS,
+ start_pos, len);
start_pos += len;
}
ASSERT(start_pos == result_len);
diff --git a/src/compiler.cc b/src/compiler.cc
index 520db34..d689e87 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -116,7 +116,6 @@
bool CompilationInfo::ShouldSelfOptimize() {
return FLAG_self_optimization &&
FLAG_crankshaft &&
- !Serializer::enabled() &&
!function()->flags()->Contains(kDontSelfOptimize) &&
!function()->flags()->Contains(kDontOptimize) &&
function()->scope()->allows_lazy_recompilation() &&
diff --git a/src/date.cc b/src/date.cc
new file mode 100644
index 0000000..a377451
--- /dev/null
+++ b/src/date.cc
@@ -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
diff --git a/src/date.h b/src/date.h
new file mode 100644
index 0000000..fcd61db
--- /dev/null
+++ b/src/date.h
@@ -0,0 +1,260 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_DATE_H_
+#define V8_DATE_H_
+
+#include "allocation.h"
+#include "globals.h"
+#include "platform.h"
+
+
+namespace v8 {
+namespace internal {
+
+class DateCache {
+ public:
+ static const int kMsPerMin = 60 * 1000;
+ static const int kSecPerDay = 24 * 60 * 60;
+ static const int64_t kMsPerDay = kSecPerDay * 1000;
+
+ // The largest time that can be passed to OS date-time library functions.
+ static const int kMaxEpochTimeInSec = kMaxInt;
+ static const int64_t kMaxEpochTimeInMs =
+ static_cast<int64_t>(kMaxInt) * 1000;
+
+ // The largest time that can be stored in JSDate.
+ static const int64_t kMaxTimeInMs =
+ static_cast<int64_t>(864000000) * 10000000;
+
+ // Conservative upper bound on time that can be stored in JSDate
+ // before UTC conversion.
+ static const int64_t kMaxTimeBeforeUTCInMs =
+ kMaxTimeInMs + 10 * kMsPerDay;
+
+ // Sentinel that denotes an invalid local offset.
+ static const int kInvalidLocalOffsetInMs = kMaxInt;
+ // Sentinel that denotes an invalid cache stamp.
+ // It is an invariant of DateCache that cache stamp is non-negative.
+ static const int kInvalidStamp = -1;
+
+ DateCache() : stamp_(0) {
+ ResetDateCache();
+ }
+
+ virtual ~DateCache() {}
+
+
+ // Clears cached timezone information and increments the cache stamp.
+ void ResetDateCache();
+
+
+ // Computes floor(time_ms / kMsPerDay).
+ static int DaysFromTime(int64_t time_ms) {
+ if (time_ms < 0) time_ms -= (kMsPerDay - 1);
+ return static_cast<int>(time_ms / kMsPerDay);
+ }
+
+
+ // Computes modulo(time_ms, kMsPerDay) given that
+ // days = floor(time_ms / kMsPerDay).
+ static int TimeInDay(int64_t time_ms, int days) {
+ return static_cast<int>(time_ms - days * kMsPerDay);
+ }
+
+
+ // Given the number of days since the epoch, computes the weekday.
+ // ECMA 262 - 15.9.1.6.
+ int Weekday(int days) {
+ int result = (days + 4) % 7;
+ return result >= 0 ? result : result + 7;
+ }
+
+
+ bool IsLeap(int year) {
+ return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
+ }
+
+
+ // ECMA 262 - 15.9.1.7.
+ int LocalOffsetInMs() {
+ if (local_offset_ms_ == kInvalidLocalOffsetInMs) {
+ local_offset_ms_ = GetLocalOffsetFromOS();
+ }
+ return local_offset_ms_;
+ }
+
+
+ const char* LocalTimezone(int64_t time_ms) {
+ if (time_ms < 0 || time_ms > kMaxEpochTimeInMs) {
+ time_ms = EquivalentTime(time_ms);
+ }
+ return OS::LocalTimezone(static_cast<double>(time_ms));
+ }
+
+ // ECMA 262 - 15.9.5.26
+ int TimezoneOffset(int64_t time_ms) {
+ int64_t local_ms = ToLocal(time_ms);
+ return static_cast<int>((time_ms - local_ms) / kMsPerMin);
+ }
+
+ // ECMA 262 - 15.9.1.9
+ int64_t ToLocal(int64_t time_ms) {
+ return time_ms + LocalOffsetInMs() + DaylightSavingsOffsetInMs(time_ms);
+ }
+
+ // ECMA 262 - 15.9.1.9
+ int64_t ToUTC(int64_t time_ms) {
+ time_ms -= LocalOffsetInMs();
+ return time_ms - DaylightSavingsOffsetInMs(time_ms);
+ }
+
+
+ // Computes a time equivalent to the given time according
+ // to ECMA 262 - 15.9.1.9.
+ // The issue here is that some library calls don't work right for dates
+ // that cannot be represented using a non-negative signed 32 bit integer
+ // (measured in whole seconds based on the 1970 epoch).
+ // We solve this by mapping the time to a year with same leap-year-ness
+ // and same starting day for the year. The ECMAscript specification says
+ // we must do this, but for compatibility with other browsers, we use
+ // the actual year if it is in the range 1970..2037
+ int64_t EquivalentTime(int64_t time_ms) {
+ int days = DaysFromTime(time_ms);
+ int time_within_day_ms = static_cast<int>(time_ms - days * kMsPerDay);
+ int year, month, day;
+ YearMonthDayFromDays(days, &year, &month, &day);
+ int new_days = DaysFromYearMonth(EquivalentYear(year), month) + day - 1;
+ return static_cast<int64_t>(new_days) * kMsPerDay + time_within_day_ms;
+ }
+
+ // Returns an equivalent year in the range [2008-2035] matching
+ // - leap year,
+ // - week day of first day.
+ // ECMA 262 - 15.9.1.9.
+ int EquivalentYear(int year) {
+ int week_day = Weekday(DaysFromYearMonth(year, 0));
+ int recent_year = (IsLeap(year) ? 1956 : 1967) + (week_day * 12) % 28;
+ // Find the year in the range 2008..2037 that is equivalent mod 28.
+ // Add 3*28 to give a positive argument to the modulus operator.
+ return 2008 + (recent_year + 3 * 28 - 2008) % 28;
+ }
+
+ // Given the number of days since the epoch, computes
+ // the corresponding year, month, and day.
+ void YearMonthDayFromDays(int days, int* year, int* month, int* day);
+
+ // Computes the number of days since the epoch for
+ // the first day of the given month in the given year.
+ int DaysFromYearMonth(int year, int month);
+
+ // Cache stamp is used for invalidating caches in JSDate.
+ // We increment the stamp each time when the timezone information changes.
+ // JSDate objects perform stamp check and invalidate their caches if
+ // their saved stamp is not equal to the current stamp.
+ Smi* stamp() { return stamp_; }
+ void* stamp_address() { return &stamp_; }
+
+ // These functions are virtual so that we can override them when testing.
+ virtual int GetDaylightSavingsOffsetFromOS(int64_t time_sec) {
+ double time_ms = static_cast<double>(time_sec * 1000);
+ return static_cast<int>(OS::DaylightSavingsOffset(time_ms));
+ }
+
+ virtual int GetLocalOffsetFromOS() {
+ double offset = OS::LocalTimeOffset();
+ ASSERT(offset < kInvalidLocalOffsetInMs);
+ return static_cast<int>(offset);
+ }
+
+ private:
+ // The implementation relies on the fact that no time zones have
+ // more than one daylight savings offset change per 19 days.
+ // In Egypt in 2010 they decided to suspend DST during Ramadan. This
+ // led to a short interval where DST is in effect from September 10 to
+ // September 30.
+ static const int kDefaultDSTDeltaInSec = 19 * kSecPerDay;
+
+ // Size of the Daylight Savings Time cache.
+ static const int kDSTSize = 32;
+
+ // Daylight Savings Time segment stores a segment of time where
+ // daylight savings offset does not change.
+ struct DST {
+ int start_sec;
+ int end_sec;
+ int offset_ms;
+ int last_used;
+ };
+
+ // Computes the daylight savings offset for the given time.
+ // ECMA 262 - 15.9.1.8
+ int DaylightSavingsOffsetInMs(int64_t time_ms);
+
+ // Sets the before_ and the after_ segments from the DST cache such that
+ // the before_ segment starts earlier than the given time and
+ // the after_ segment start later than the given time.
+ // Both segments might be invalid.
+ // The last_used counters of the before_ and after_ are updated.
+ void ProbeDST(int time_sec);
+
+ // Finds the least recently used segment from the DST cache that is not
+ // equal to the given 'skip' segment.
+ DST* LeastRecentlyUsedDST(DST* skip);
+
+ // Extends the after_ segment with the given point or resets it
+ // if it starts later than the given time + kDefaultDSTDeltaInSec.
+ inline void ExtendTheAfterSegment(int time_sec, int offset_ms);
+
+ // Makes the given segment invalid.
+ inline void ClearSegment(DST* segment);
+
+ bool InvalidSegment(DST* segment) {
+ return segment->start_sec > segment->end_sec;
+ }
+
+ Smi* stamp_;
+
+ // Daylight Saving Time cache.
+ DST dst_[kDSTSize];
+ int dst_usage_counter_;
+ DST* before_;
+ DST* after_;
+
+ int local_offset_ms_;
+
+ // Year/Month/Day cache.
+ bool ymd_valid_;
+ int ymd_days_;
+ int ymd_year_;
+ int ymd_month_;
+ int ymd_day_;
+};
+
+} } // namespace v8::internal
+
+#endif
diff --git a/src/date.js b/src/date.js
index 8c51a93..75edf6d 100644
--- a/src/date.js
+++ b/src/date.js
@@ -44,173 +44,6 @@
throw new $TypeError('this is not a Date object.');
}
-// ECMA 262 - 5.2
-function Modulo(value, remainder) {
- var mod = value % remainder;
- // Guard against returning -0.
- if (mod == 0) return 0;
- return mod >= 0 ? mod : mod + remainder;
-}
-
-
-function TimeWithinDay(time) {
- return Modulo(time, msPerDay);
-}
-
-
-// ECMA 262 - 15.9.1.3
-function DaysInYear(year) {
- if (year % 4 != 0) return 365;
- if ((year % 100 == 0) && (year % 400 != 0)) return 365;
- return 366;
-}
-
-
-function DayFromYear(year) {
- return 365 * (year-1970)
- + FLOOR((year-1969)/4)
- - FLOOR((year-1901)/100)
- + FLOOR((year-1601)/400);
-}
-
-
-function TimeFromYear(year) {
- return msPerDay * DayFromYear(year);
-}
-
-
-function InLeapYear(time) {
- return DaysInYear(YearFromTime(time)) - 365; // Returns 1 or 0.
-}
-
-
-// ECMA 262 - 15.9.1.9
-function EquivalentYear(year) {
- // Returns an equivalent year in the range [2008-2035] matching
- // - leap year.
- // - week day of first day.
- var time = TimeFromYear(year);
- var recent_year = (InLeapYear(time) == 0 ? 1967 : 1956) +
- (WeekDay(time) * 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;
-}
-
-
-function EquivalentTime(t) {
- // 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
- if (t >= 0 && t <= 2.1e12) return t;
-
- var day = MakeDay(EquivalentYear(YearFromTime(t)),
- MonthFromTime(t),
- DateFromTime(t));
- return MakeDate(day, TimeWithinDay(t));
-}
-
-
-// local_time_offset is initialized when the DST_offset_cache is missed.
-// It must not be used until after a call to DaylightSavingsOffset().
-// In this way, only one check, for a DST cache miss, is needed.
-var local_time_offset;
-
-
-// Because computing the DST offset is an expensive operation,
-// we keep a cache of the last computed DST offset along with a time interval
-// where we know the cache is valid.
-// When the cache is valid, local_time_offset is also valid.
-var DST_offset_cache = {
- // Cached DST offset.
- offset: 0,
- // Time interval where the cached offset is valid.
- start: 0, end: -1,
- // Size of next interval expansion.
- increment: 0,
- initial_increment: 19 * msPerDay
-};
-
-
-// NOTE: 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.
-//
-// If this function is called with NaN it returns NaN.
-function DaylightSavingsOffset(t) {
- // Load the cache object from the builtins object.
- var cache = DST_offset_cache;
-
- // Cache the start and the end in local variables for fast access.
- var start = cache.start;
- var end = cache.end;
-
- if (start <= t) {
- // If the time fits in the cached interval, return the cached offset.
- if (t <= end) return cache.offset;
-
- // If the cache misses, the local_time_offset may not be initialized.
- if (IS_UNDEFINED(local_time_offset)) {
- local_time_offset = %DateLocalTimeOffset();
- }
-
- // Compute a possible new interval end.
- var new_end = end + cache.increment;
-
- if (t <= new_end) {
- var end_offset = %DateDaylightSavingsOffset(EquivalentTime(new_end));
- if (cache.offset == end_offset) {
- // If the offset at the end of the new interval still matches
- // the offset in the cache, we grow the cached time interval
- // and return the offset.
- cache.end = new_end;
- cache.increment = cache.initial_increment;
- return end_offset;
- } else {
- var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
- if (offset == end_offset) {
- // The offset at the given time is equal to the offset at the
- // new end of the interval, so that means that we've just skipped
- // the point in time where the DST offset change occurred. Updated
- // the interval to reflect this and reset the increment.
- cache.start = t;
- cache.end = new_end;
- cache.increment = cache.initial_increment;
- } else {
- // The interval contains a DST offset change and the given time is
- // before it. Adjust the increment to avoid a linear search for
- // the offset change point and change the end of the interval.
- cache.increment /= 3;
- cache.end = t;
- }
- // Update the offset in the cache and return it.
- cache.offset = offset;
- return offset;
- }
- }
- }
-
- // If the cache misses, the local_time_offset may not be initialized.
- if (IS_UNDEFINED(local_time_offset)) {
- local_time_offset = %DateLocalTimeOffset();
- }
- // Compute the DST offset for the time and shrink the cache interval
- // to only contain the time. This allows fast repeated DST offset
- // computations for the same time.
- var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
- cache.offset = offset;
- cache.start = cache.end = t;
- cache.increment = cache.initial_increment;
- return offset;
-}
-
var timezone_cache_time = $NaN;
var timezone_cache_timezone;
@@ -220,57 +53,18 @@
if (t == timezone_cache_time) {
return timezone_cache_timezone;
}
- var timezone = %DateLocalTimezone(EquivalentTime(t));
+ var timezone = %DateLocalTimezone(t);
timezone_cache_time = t;
timezone_cache_timezone = timezone;
return timezone;
}
-function WeekDay(time) {
- return Modulo(DAY(time) + 4, 7);
-}
-
-
-function LocalTime(time) {
- if (NUMBER_IS_NAN(time)) return time;
- // DaylightSavingsOffset called before local_time_offset used.
- return time + DaylightSavingsOffset(time) + local_time_offset;
-}
-
-
-var ltcache = {
- key: null,
- val: null
-};
-
-function LocalTimeNoCheck(time) {
- var ltc = ltcache;
- if (%_ObjectEquals(time, ltc.key)) return ltc.val;
-
- // Inline the DST offset cache checks for speed.
- // The cache is hit, or DaylightSavingsOffset is called,
- // before local_time_offset is used.
- var cache = DST_offset_cache;
- if (cache.start <= time && time <= cache.end) {
- var dst_offset = cache.offset;
- } else {
- var dst_offset = DaylightSavingsOffset(time);
- }
- ltc.key = time;
- return (ltc.val = time + local_time_offset + dst_offset);
-}
-
-
function UTC(time) {
if (NUMBER_IS_NAN(time)) return time;
// local_time_offset is needed before the call to DaylightSavingsOffset,
// so it may be uninitialized.
- if (IS_UNDEFINED(local_time_offset)) {
- local_time_offset = %DateLocalTimeOffset();
- }
- var tmp = time - local_time_offset;
- return tmp - DaylightSavingsOffset(tmp);
+ return %DateToUTC(time);
}
@@ -293,48 +87,6 @@
}
-var ymd_from_time_cache = [1970, 0, 1];
-var ymd_from_time_cached_time = 0;
-
-function YearFromTime(t) {
- if (t !== ymd_from_time_cached_time) {
- if (!$isFinite(t)) {
- return $NaN;
- }
-
- %DateYMDFromTime(t, ymd_from_time_cache);
- ymd_from_time_cached_time = t;
- }
-
- return ymd_from_time_cache[0];
-}
-
-function MonthFromTime(t) {
- if (t !== ymd_from_time_cached_time) {
- if (!$isFinite(t)) {
- return $NaN;
- }
- %DateYMDFromTime(t, ymd_from_time_cache);
- ymd_from_time_cached_time = t;
- }
-
- return ymd_from_time_cache[1];
-}
-
-function DateFromTime(t) {
- if (t !== ymd_from_time_cached_time) {
- if (!$isFinite(t)) {
- return $NaN;
- }
-
- %DateYMDFromTime(t, ymd_from_time_cache);
- ymd_from_time_cached_time = t;
- }
-
- return ymd_from_time_cache[2];
-}
-
-
// Compute number of days given a year, month, date.
// Note that month and date can lie outside the normal range.
// For example:
@@ -385,9 +137,6 @@
var Date_cache = {
// Cached time value.
time: $NaN,
- // Cached year when interpreting the time as a local time. Only
- // valid when the time matches cached time.
- year: $NaN,
// String input for which the cached time is valid.
string: null
};
@@ -404,11 +153,10 @@
var value;
if (argc == 0) {
value = %DateCurrentTime();
-
+ SET_UTC_DATE_VALUE(this, value);
} else if (argc == 1) {
if (IS_NUMBER(year)) {
- value = TimeClip(year);
-
+ value = year;
} else if (IS_STRING(year)) {
// Probe the Date cache. If we already have a time value for the
// given time, we re-use that instead of parsing the string again.
@@ -419,7 +167,6 @@
value = DateParse(year);
if (!NUMBER_IS_NAN(value)) {
cache.time = value;
- cache.year = YearFromTime(LocalTimeNoCheck(value));
cache.string = year;
}
}
@@ -433,9 +180,9 @@
// which is the default for everything else than Date objects.
// This makes us behave like KJS and SpiderMonkey.
var time = ToPrimitive(year, NUMBER_HINT);
- value = IS_STRING(time) ? DateParse(time) : TimeClip(ToNumber(time));
+ value = IS_STRING(time) ? DateParse(time) : ToNumber(time);
}
-
+ SET_UTC_DATE_VALUE(this, value);
} else {
year = ToNumber(year);
month = ToNumber(month);
@@ -449,9 +196,9 @@
TO_INTEGER(year) <= 99) ? 1900 + TO_INTEGER(year) : year;
var day = MakeDay(year, month, date);
var time = MakeTime(hours, minutes, seconds, ms);
- value = TimeClip(UTC(MakeDate(day, time)));
+ value = MakeDate(day, time);
+ SET_LOCAL_DATE_VALUE(this, value);
}
- %_SetValueOf(this, value);
});
@@ -468,11 +215,11 @@
}
-function DateString(time) {
- return WeekDays[WeekDay(time)] + ' '
- + Months[MonthFromTime(time)] + ' '
- + TwoDigitString(DateFromTime(time)) + ' '
- + YearFromTime(time);
+function DateString(date) {
+ return WeekDays[LOCAL_WEEKDAY(date)] + ' '
+ + Months[LOCAL_MONTH(date)] + ' '
+ + TwoDigitString(LOCAL_DAY(date)) + ' '
+ + LOCAL_YEAR(date);
}
@@ -482,38 +229,32 @@
'July', 'August', 'September', 'October', 'November', 'December'];
-function LongDateString(time) {
- return LongWeekDays[WeekDay(time)] + ', '
- + LongMonths[MonthFromTime(time)] + ' '
- + TwoDigitString(DateFromTime(time)) + ', '
- + YearFromTime(time);
+function LongDateString(date) {
+ return LongWeekDays[LOCAL_WEEKDAY(date)] + ', '
+ + LongMonths[LOCAL_MONTH(date)] + ' '
+ + TwoDigitString(LOCAL_DAY(date)) + ', '
+ + LOCAL_YEAR(date);
}
-function TimeString(time) {
- return TwoDigitString(HOUR_FROM_TIME(time)) + ':'
- + TwoDigitString(MIN_FROM_TIME(time)) + ':'
- + TwoDigitString(SEC_FROM_TIME(time));
+function TimeString(date) {
+ return TwoDigitString(LOCAL_HOUR(date)) + ':'
+ + TwoDigitString(LOCAL_MIN(date)) + ':'
+ + TwoDigitString(LOCAL_SEC(date));
}
-function LocalTimezoneString(time) {
- var old_timezone = timezone_cache_timezone;
- var timezone = LocalTimezone(time);
- if (old_timezone && timezone != old_timezone) {
- // If the timezone string has changed from the one that we cached,
- // the local time offset may now be wrong. So we need to update it
- // and try again.
- local_time_offset = %DateLocalTimeOffset();
- // We also need to invalidate the DST cache as the new timezone may have
- // different DST times.
- var dst_cache = DST_offset_cache;
- dst_cache.start = 0;
- dst_cache.end = -1;
- }
+function TimeStringUTC(date) {
+ return TwoDigitString(UTC_HOUR(date)) + ':'
+ + TwoDigitString(UTC_MIN(date)) + ':'
+ + TwoDigitString(UTC_SEC(date));
+}
- var timezoneOffset =
- (DaylightSavingsOffset(time) + local_time_offset) / msPerMinute;
+
+function LocalTimezoneString(date) {
+ var timezone = LocalTimezone(UTC_DATE_VALUE(date));
+
+ var timezoneOffset = -TIMEZONE_OFFSET(date);
var sign = (timezoneOffset >= 0) ? 1 : -1;
var hours = FLOOR((sign * timezoneOffset)/60);
var min = FLOOR((sign * timezoneOffset)%60);
@@ -523,8 +264,8 @@
}
-function DatePrintString(time) {
- return DateString(time) + ' ' + TimeString(time);
+function DatePrintString(date) {
+ return DateString(date) + ' ' + TimeString(date);
}
// -------------------------------------------------------------------
@@ -564,7 +305,7 @@
TO_INTEGER(year) <= 99) ? 1900 + TO_INTEGER(year) : year;
var day = MakeDay(year, month, date);
var time = MakeTime(hours, minutes, seconds, ms);
- return %_SetValueOf(this, TimeClip(MakeDate(day, time)));
+ return TimeClip(MakeDate(day, time));
}
@@ -577,27 +318,30 @@
// ECMA 262 - 15.9.5.2
function DateToString() {
- var t = DATE_VALUE(this);
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this)
if (NUMBER_IS_NAN(t)) return kInvalidDate;
- var time_zone_string = LocalTimezoneString(t); // May update local offset.
- return DatePrintString(LocalTimeNoCheck(t)) + time_zone_string;
+ var time_zone_string = LocalTimezoneString(this)
+ return DatePrintString(this) + time_zone_string;
}
// ECMA 262 - 15.9.5.3
function DateToDateString() {
- var t = DATE_VALUE(this);
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this);
if (NUMBER_IS_NAN(t)) return kInvalidDate;
- return DateString(LocalTimeNoCheck(t));
+ return DateString(this);
}
// ECMA 262 - 15.9.5.4
function DateToTimeString() {
- var t = DATE_VALUE(this);
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this);
if (NUMBER_IS_NAN(t)) return kInvalidDate;
- var time_zone_string = LocalTimezoneString(t); // May update local offset.
- return TimeString(LocalTimeNoCheck(t)) + time_zone_string;
+ var time_zone_string = LocalTimezoneString(this);
+ return TimeString(this) + time_zone_string;
}
@@ -609,363 +353,388 @@
// ECMA 262 - 15.9.5.6
function DateToLocaleDateString() {
- var t = DATE_VALUE(this);
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this);
if (NUMBER_IS_NAN(t)) return kInvalidDate;
- return LongDateString(LocalTimeNoCheck(t));
+ return LongDateString(this);
}
// ECMA 262 - 15.9.5.7
function DateToLocaleTimeString() {
- var t = DATE_VALUE(this);
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this);
if (NUMBER_IS_NAN(t)) return kInvalidDate;
- var lt = LocalTimeNoCheck(t);
- return TimeString(lt);
+ return TimeString(this);
}
// ECMA 262 - 15.9.5.8
function DateValueOf() {
- return DATE_VALUE(this);
+ CHECK_DATE(this);
+ return UTC_DATE_VALUE(this);
}
// ECMA 262 - 15.9.5.9
function DateGetTime() {
- return DATE_VALUE(this);
+ CHECK_DATE(this);
+ return UTC_DATE_VALUE(this);
}
// ECMA 262 - 15.9.5.10
function DateGetFullYear() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- var cache = Date_cache;
- if (cache.time === t) return cache.year;
- return YearFromTime(LocalTimeNoCheck(t));
+ CHECK_DATE(this);
+ return LOCAL_YEAR(this);
}
// ECMA 262 - 15.9.5.11
function DateGetUTCFullYear() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- return YearFromTime(t);
+ CHECK_DATE(this);
+ return UTC_YEAR(this);
}
// ECMA 262 - 15.9.5.12
function DateGetMonth() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- return MonthFromTime(LocalTimeNoCheck(t));
+ CHECK_DATE(this);
+ return LOCAL_MONTH(this);
}
// ECMA 262 - 15.9.5.13
function DateGetUTCMonth() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- return MonthFromTime(t);
+ CHECK_DATE(this);
+ return UTC_MONTH(this);
}
// ECMA 262 - 15.9.5.14
function DateGetDate() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- return DateFromTime(LocalTimeNoCheck(t));
+ CHECK_DATE(this);
+ return LOCAL_DAY(this);
}
// ECMA 262 - 15.9.5.15
function DateGetUTCDate() {
- var t = DATE_VALUE(this);
- return NAN_OR_DATE_FROM_TIME(t);
+ CHECK_DATE(this);
+ return UTC_DAY(this);
}
// ECMA 262 - 15.9.5.16
function DateGetDay() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- return WeekDay(LocalTimeNoCheck(t));
+ CHECK_DATE(this);
+ return LOCAL_WEEKDAY(this);
}
// ECMA 262 - 15.9.5.17
function DateGetUTCDay() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- return WeekDay(t);
+ CHECK_DATE(this);
+ return UTC_WEEKDAY(this);
}
// ECMA 262 - 15.9.5.18
function DateGetHours() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- return HOUR_FROM_TIME(LocalTimeNoCheck(t));
+ CHECK_DATE(this);
+ return LOCAL_HOUR(this);
}
// ECMA 262 - 15.9.5.19
function DateGetUTCHours() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- return HOUR_FROM_TIME(t);
+ CHECK_DATE(this);
+ return UTC_HOUR(this);
}
// ECMA 262 - 15.9.5.20
function DateGetMinutes() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- return MIN_FROM_TIME(LocalTimeNoCheck(t));
+ CHECK_DATE(this);
+ return LOCAL_MIN(this);
}
// ECMA 262 - 15.9.5.21
function DateGetUTCMinutes() {
- var t = DATE_VALUE(this);
- return NAN_OR_MIN_FROM_TIME(t);
+ CHECK_DATE(this);
+ return UTC_MIN(this);
}
// ECMA 262 - 15.9.5.22
function DateGetSeconds() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- return SEC_FROM_TIME(LocalTimeNoCheck(t));
+ CHECK_DATE(this);
+ return LOCAL_SEC(this);
}
// ECMA 262 - 15.9.5.23
function DateGetUTCSeconds() {
- var t = DATE_VALUE(this);
- return NAN_OR_SEC_FROM_TIME(t);
+ CHECK_DATE(this);
+ return UTC_SEC(this)
}
// ECMA 262 - 15.9.5.24
function DateGetMilliseconds() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- return MS_FROM_TIME(LocalTimeNoCheck(t));
+ CHECK_DATE(this);
+ return LOCAL_MS(this);
}
// ECMA 262 - 15.9.5.25
function DateGetUTCMilliseconds() {
- var t = DATE_VALUE(this);
- return NAN_OR_MS_FROM_TIME(t);
+ CHECK_DATE(this);
+ return UTC_MS(this);
}
// ECMA 262 - 15.9.5.26
function DateGetTimezoneOffset() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- return (t - LocalTimeNoCheck(t)) / msPerMinute;
+ CHECK_DATE(this);
+ return TIMEZONE_OFFSET(this);
}
// ECMA 262 - 15.9.5.27
function DateSetTime(ms) {
- if (!IS_DATE(this)) ThrowDateTypeError();
- return %_SetValueOf(this, TimeClip(ToNumber(ms)));
+ CHECK_DATE(this);
+ SET_UTC_DATE_VALUE(this, ToNumber(ms));
+ return UTC_DATE_VALUE(this);
}
// ECMA 262 - 15.9.5.28
function DateSetMilliseconds(ms) {
- var t = LocalTime(DATE_VALUE(this));
+ CHECK_DATE(this);
+ var t = LOCAL_DATE_VALUE(this);
ms = ToNumber(ms);
- var time = MakeTime(HOUR_FROM_TIME(t),
- MIN_FROM_TIME(t),
- SEC_FROM_TIME(t),
- ms);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
+ var time = MakeTime(LOCAL_HOUR(this), LOCAL_MIN(this), LOCAL_SEC(this), ms);
+ SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
+ return this;
}
// ECMA 262 - 15.9.5.29
function DateSetUTCMilliseconds(ms) {
- var t = DATE_VALUE(this);
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this);
ms = ToNumber(ms);
- var time = MakeTime(HOUR_FROM_TIME(t),
- MIN_FROM_TIME(t),
- SEC_FROM_TIME(t),
+ var time = MakeTime(UTC_HOUR(this),
+ UTC_MIN(this),
+ UTC_SEC(this),
ms);
- return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
+ return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
}
// ECMA 262 - 15.9.5.30
function DateSetSeconds(sec, ms) {
- var t = LocalTime(DATE_VALUE(this));
+ CHECK_DATE(this);
+ var t = LOCAL_DATE_VALUE(this);
sec = ToNumber(sec);
- ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
- var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
+ ms = %_ArgumentsLength() < 2 ? LOCAL_MS(this) : ToNumber(ms);
+ var time = MakeTime(LOCAL_HOUR(this), LOCAL_MIN(this), sec, ms);
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
}
// ECMA 262 - 15.9.5.31
function DateSetUTCSeconds(sec, ms) {
- var t = DATE_VALUE(this);
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this);
sec = ToNumber(sec);
- ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
- var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
- return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
+ ms = %_ArgumentsLength() < 2 ? UTC_MS(this) : ToNumber(ms);
+ var time = MakeTime(UTC_HOUR(this), UTC_MIN(this), sec, ms);
+ return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
}
// ECMA 262 - 15.9.5.33
function DateSetMinutes(min, sec, ms) {
- var t = LocalTime(DATE_VALUE(this));
+ CHECK_DATE(this);
+ var t = LOCAL_DATE_VALUE(this);
min = ToNumber(min);
var argc = %_ArgumentsLength();
- sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
- ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
- var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
+ sec = argc < 2 ? LOCAL_SEC(this) : ToNumber(sec);
+ ms = argc < 3 ? LOCAL_MS(this) : ToNumber(ms);
+ var time = MakeTime(LOCAL_HOUR(this), min, sec, ms);
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
}
// ECMA 262 - 15.9.5.34
function DateSetUTCMinutes(min, sec, ms) {
- var t = DATE_VALUE(this);
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this);
min = ToNumber(min);
var argc = %_ArgumentsLength();
- sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
- ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
- var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
- return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
+ sec = argc < 2 ? UTC_SEC(this) : ToNumber(sec);
+ ms = argc < 3 ? UTC_MS(this) : ToNumber(ms);
+ var time = MakeTime(UTC_HOUR(this), min, sec, ms);
+ return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
}
// ECMA 262 - 15.9.5.35
function DateSetHours(hour, min, sec, ms) {
- var t = LocalTime(DATE_VALUE(this));
+ CHECK_DATE(this);
+ var t = LOCAL_DATE_VALUE(this);
hour = ToNumber(hour);
var argc = %_ArgumentsLength();
- min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min);
- sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
- ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
+ min = argc < 2 ? LOCAL_MIN(this) : ToNumber(min);
+ sec = argc < 3 ? LOCAL_SEC(this) : ToNumber(sec);
+ ms = argc < 4 ? LOCAL_MS(this) : ToNumber(ms);
var time = MakeTime(hour, min, sec, ms);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
}
// ECMA 262 - 15.9.5.34
function DateSetUTCHours(hour, min, sec, ms) {
- var t = DATE_VALUE(this);
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this);
hour = ToNumber(hour);
var argc = %_ArgumentsLength();
- min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min);
- sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
- ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
+ min = argc < 2 ? UTC_MIN(this) : ToNumber(min);
+ sec = argc < 3 ? UTC_SEC(this) : ToNumber(sec);
+ ms = argc < 4 ? UTC_MS(this) : ToNumber(ms);
var time = MakeTime(hour, min, sec, ms);
- return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
+ return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
}
// ECMA 262 - 15.9.5.36
function DateSetDate(date) {
- var t = LocalTime(DATE_VALUE(this));
+ CHECK_DATE(this);
+ var t = LOCAL_DATE_VALUE(this);
date = ToNumber(date);
- var day = MakeDay(YearFromTime(t), MonthFromTime(t), date);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
+ var day = MakeDay(LOCAL_YEAR(this), LOCAL_MONTH(this), date);
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(day, LOCAL_TIME_IN_DAY(this)));
}
// ECMA 262 - 15.9.5.37
function DateSetUTCDate(date) {
- var t = DATE_VALUE(this);
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this);
date = ToNumber(date);
- var day = MakeDay(YearFromTime(t), MonthFromTime(t), date);
- return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
+ var day = MakeDay(UTC_YEAR(this), UTC_MONTH(this), date);
+ return SET_UTC_DATE_VALUE(this, MakeDate(day, UTC_TIME_IN_DAY(this)));
}
// ECMA 262 - 15.9.5.38
function DateSetMonth(month, date) {
- var t = LocalTime(DATE_VALUE(this));
+ CHECK_DATE(this);
+ var t = LOCAL_DATE_VALUE(this);
month = ToNumber(month);
- date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date);
- var day = MakeDay(YearFromTime(t), month, date);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
+ date = %_ArgumentsLength() < 2 ? LOCAL_DAY(this) : ToNumber(date);
+ var day = MakeDay(LOCAL_YEAR(this), month, date);
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(day, LOCAL_TIME_IN_DAY(this)));
}
// ECMA 262 - 15.9.5.39
function DateSetUTCMonth(month, date) {
- var t = DATE_VALUE(this);
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this);
month = ToNumber(month);
- date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date);
- var day = MakeDay(YearFromTime(t), month, date);
- return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
+ date = %_ArgumentsLength() < 2 ? UTC_DAY(this) : ToNumber(date);
+ var day = MakeDay(UTC_YEAR(this), month, date);
+ return SET_UTC_DATE_VALUE(this, MakeDate(day, UTC_TIME_IN_DAY(this)));
}
// ECMA 262 - 15.9.5.40
function DateSetFullYear(year, month, date) {
- var t = DATE_VALUE(this);
- t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t);
+ CHECK_DATE(this);
+ var t = LOCAL_DATE_VALUE(this);
year = ToNumber(year);
var argc = %_ArgumentsLength();
- month = argc < 2 ? MonthFromTime(t) : ToNumber(month);
- date = argc < 3 ? DateFromTime(t) : ToNumber(date);
+ var time ;
+ if (NUMBER_IS_NAN(t)) {
+ month = argc < 2 ? 0 : ToNumber(month);
+ date = argc < 3 ? 1 : ToNumber(date);
+ time = 0;
+ } else {
+ month = argc < 2 ? LOCAL_MONTH(this) : ToNumber(month);
+ date = argc < 3 ? LOCAL_DAY(this) : ToNumber(date);
+ time = LOCAL_TIME_IN_DAY(this);
+ }
var day = MakeDay(year, month, date);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(day, time));
}
// ECMA 262 - 15.9.5.41
function DateSetUTCFullYear(year, month, date) {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) t = 0;
- var argc = %_ArgumentsLength();
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this);
year = ToNumber(year);
- month = argc < 2 ? MonthFromTime(t) : ToNumber(month);
- date = argc < 3 ? DateFromTime(t) : ToNumber(date);
+ var argc = %_ArgumentsLength();
+ var time ;
+ if (NUMBER_IS_NAN(t)) {
+ month = argc < 2 ? 0 : ToNumber(month);
+ date = argc < 3 ? 1 : ToNumber(date);
+ time = 0;
+ } else {
+ month = argc < 2 ? UTC_MONTH(this) : ToNumber(month);
+ date = argc < 3 ? UTC_DAY(this) : ToNumber(date);
+ time = UTC_TIME_IN_DAY(this);
+ }
var day = MakeDay(year, month, date);
- return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
+ return SET_UTC_DATE_VALUE(this, MakeDate(day, time));
}
// ECMA 262 - 15.9.5.42
function DateToUTCString() {
- var t = DATE_VALUE(this);
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this);
if (NUMBER_IS_NAN(t)) return kInvalidDate;
// Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT
- return WeekDays[WeekDay(t)] + ', '
- + TwoDigitString(DateFromTime(t)) + ' '
- + Months[MonthFromTime(t)] + ' '
- + YearFromTime(t) + ' '
- + TimeString(t) + ' GMT';
+ return WeekDays[UTC_WEEKDAY(this)] + ', '
+ + TwoDigitString(UTC_DAY(this)) + ' '
+ + Months[UTC_MONTH(this)] + ' '
+ + UTC_YEAR(this) + ' '
+ + TimeStringUTC(this) + ' GMT';
}
// ECMA 262 - B.2.4
function DateGetYear() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return $NaN;
- return YearFromTime(LocalTimeNoCheck(t)) - 1900;
+ CHECK_DATE(this);
+ return LOCAL_YEAR(this) - 1900;
}
// ECMA 262 - B.2.5
function DateSetYear(year) {
- var t = LocalTime(DATE_VALUE(this));
- if (NUMBER_IS_NAN(t)) t = 0;
+ CHECK_DATE(this);
year = ToNumber(year);
- if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN);
+ if (NUMBER_IS_NAN(year)) return SET_UTC_DATE_VALUE(this, $NaN);
year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
? 1900 + TO_INTEGER(year) : year;
- var day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
+ var t = LOCAL_DATE_VALUE(this);
+ var month, date, time;
+ if (NUMBER_IS_NAN(t)) {
+ month = 0;
+ date = 1;
+ time = 0;
+ } else {
+ month = LOCAL_MONTH(this);
+ date = LOCAL_DAY(this);
+ time = LOCAL_TIME_IN_DAY(this);
+ }
+ var day = MakeDay(year, month, date);
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(day, time));
}
@@ -989,7 +758,8 @@
// ECMA 262 - 15.9.5.43
function DateToISOString() {
- var t = DATE_VALUE(this);
+ CHECK_DATE(this);
+ var t = UTC_DATE_VALUE(this);
if (NUMBER_IS_NAN(t)) throw MakeRangeError("invalid_time_value", []);
var year = this.getUTCFullYear();
var year_string;
@@ -1024,34 +794,13 @@
function ResetDateCache() {
-
- // Reset the local_time_offset:
- local_time_offset = %DateLocalTimeOffset();
-
- // Reset the DST offset cache:
- var cache = DST_offset_cache;
- cache.offset = 0;
- cache.start = 0;
- cache.end = -1;
- cache.increment = 0;
- cache.initial_increment = 19 * msPerDay;
-
// Reset the timezone cache:
timezone_cache_time = $NaN;
timezone_cache_timezone = undefined;
- // Reset the ltcache:
- ltcache.key = null;
- ltcache.val = null;
-
- // Reset the ymd_from_time_cache:
- ymd_from_time_cache = [$NaN, $NaN, $NaN];
- ymd_from_time_cached_time = $NaN;
-
// Reset the date cache:
cache = Date_cache;
cache.time = $NaN;
- cache.year = $NaN;
cache.string = null;
}
diff --git a/src/elements.cc b/src/elements.cc
index e1be677..331f6bc 100644
--- a/src/elements.cc
+++ b/src/elements.cc
@@ -59,6 +59,53 @@
namespace internal {
+// First argument in list is the accessor class, the second argument is the
+// accessor ElementsKind, and the third is the backing store class. Use the
+// fast element handler for smi-only arrays. The implementation is currently
+// identical. Note that the order must match that of the ElementsKind enum for
+// the |accessor_array[]| below to work.
+#define ELEMENTS_LIST(V) \
+ V(FastObjectElementsAccessor, FAST_SMI_ONLY_ELEMENTS, FixedArray) \
+ V(FastObjectElementsAccessor, FAST_ELEMENTS, FixedArray) \
+ V(FastDoubleElementsAccessor, FAST_DOUBLE_ELEMENTS, FixedDoubleArray) \
+ V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS, \
+ SeededNumberDictionary) \
+ V(NonStrictArgumentsElementsAccessor, NON_STRICT_ARGUMENTS_ELEMENTS, \
+ FixedArray) \
+ V(ExternalByteElementsAccessor, EXTERNAL_BYTE_ELEMENTS, \
+ ExternalByteArray) \
+ V(ExternalUnsignedByteElementsAccessor, \
+ EXTERNAL_UNSIGNED_BYTE_ELEMENTS, ExternalUnsignedByteArray) \
+ V(ExternalShortElementsAccessor, EXTERNAL_SHORT_ELEMENTS, \
+ ExternalShortArray) \
+ V(ExternalUnsignedShortElementsAccessor, \
+ EXTERNAL_UNSIGNED_SHORT_ELEMENTS, ExternalUnsignedShortArray) \
+ V(ExternalIntElementsAccessor, EXTERNAL_INT_ELEMENTS, \
+ ExternalIntArray) \
+ V(ExternalUnsignedIntElementsAccessor, \
+ EXTERNAL_UNSIGNED_INT_ELEMENTS, ExternalUnsignedIntArray) \
+ V(ExternalFloatElementsAccessor, \
+ EXTERNAL_FLOAT_ELEMENTS, ExternalFloatArray) \
+ V(ExternalDoubleElementsAccessor, \
+ EXTERNAL_DOUBLE_ELEMENTS, ExternalDoubleArray) \
+ V(PixelElementsAccessor, EXTERNAL_PIXEL_ELEMENTS, ExternalPixelArray)
+
+
+template<ElementsKind Kind> class ElementsKindTraits {
+ public:
+ typedef FixedArrayBase BackingStore;
+};
+
+#define ELEMENTS_TRAITS(Class, KindParam, Store) \
+template<> class ElementsKindTraits<KindParam> { \
+ public: \
+ static const ElementsKind Kind = KindParam; \
+ typedef Store BackingStore; \
+};
+ELEMENTS_LIST(ELEMENTS_TRAITS)
+#undef ELEMENTS_TRAITS
+
+
ElementsAccessor** ElementsAccessor::elements_accessors_;
@@ -84,6 +131,140 @@
}
+void CopyObjectToObjectElements(AssertNoAllocation* no_gc,
+ FixedArray* from_obj,
+ ElementsKind from_kind,
+ uint32_t from_start,
+ FixedArray* to_obj,
+ ElementsKind to_kind,
+ uint32_t to_start,
+ int copy_size) {
+ ASSERT(to_obj->map() != HEAP->fixed_cow_array_map());
+ ASSERT(from_kind == FAST_ELEMENTS || from_kind == FAST_SMI_ONLY_ELEMENTS);
+ ASSERT(to_kind == FAST_ELEMENTS || to_kind == FAST_SMI_ONLY_ELEMENTS);
+ if (copy_size == -1) {
+ copy_size = Min(from_obj->length() - from_start,
+ to_obj->length() - to_start);
+ }
+ ASSERT(((copy_size + static_cast<int>(to_start)) <= to_obj->length() &&
+ (copy_size + static_cast<int>(from_start)) <= from_obj->length()));
+ if (copy_size == 0) return;
+ Address to = to_obj->address() + FixedArray::kHeaderSize;
+ Address from = from_obj->address() + FixedArray::kHeaderSize;
+ CopyWords(reinterpret_cast<Object**>(to) + to_start,
+ reinterpret_cast<Object**>(from) + from_start,
+ copy_size);
+ if (from_kind == FAST_ELEMENTS && to_kind == FAST_ELEMENTS) {
+ Heap* heap = from_obj->GetHeap();
+ WriteBarrierMode mode = to_obj->GetWriteBarrierMode(*no_gc);
+ if (mode == UPDATE_WRITE_BARRIER) {
+ heap->RecordWrites(to_obj->address(),
+ to_obj->OffsetOfElementAt(to_start),
+ copy_size);
+ }
+ heap->incremental_marking()->RecordWrites(to_obj);
+ }
+}
+
+
+
+
+static void CopyDictionaryToObjectElements(SeededNumberDictionary* from,
+ uint32_t from_start,
+ FixedArray* to,
+ ElementsKind to_kind,
+ uint32_t to_start,
+ int copy_size) {
+ ASSERT(to != from);
+ ASSERT(to_kind == FAST_ELEMENTS || to_kind == FAST_SMI_ONLY_ELEMENTS);
+ ASSERT(copy_size == -1 ||
+ (copy_size + static_cast<int>(to_start)) <= to->length());
+ WriteBarrierMode mode = to_kind == FAST_ELEMENTS
+ ? UPDATE_WRITE_BARRIER
+ : SKIP_WRITE_BARRIER;
+ uint32_t copy_limit = (copy_size == -1)
+ ? to->length()
+ : Min(to_start + copy_size, static_cast<uint32_t>(to->length()));
+ for (int i = 0; i < from->Capacity(); ++i) {
+ Object* key = from->KeyAt(i);
+ if (key->IsNumber()) {
+ uint32_t entry = static_cast<uint32_t>(key->Number());
+ if (entry >= to_start && entry < copy_limit) {
+ Object* value = from->ValueAt(i);
+ ASSERT(to_kind == FAST_ELEMENTS || value->IsSmi());
+ to->set(entry, value, mode);
+ }
+ }
+ }
+}
+
+
+MUST_USE_RESULT static MaybeObject* CopyDoubleToObjectElements(
+ FixedDoubleArray* from_obj,
+ uint32_t from_start,
+ FixedArray* to_obj,
+ ElementsKind to_kind,
+ uint32_t to_start,
+ int copy_size) {
+ ASSERT(to_kind == FAST_ELEMENTS || to_kind == FAST_SMI_ONLY_ELEMENTS);
+ if (copy_size == -1) {
+ copy_size = Min(from_obj->length() - from_start,
+ to_obj->length() - to_start);
+ }
+ ASSERT(((copy_size + static_cast<int>(to_start)) <= to_obj->length() &&
+ (copy_size + static_cast<int>(from_start)) <= from_obj->length()));
+ if (copy_size == 0) return from_obj;
+ for (int i = 0; i < copy_size; ++i) {
+ if (to_kind == FAST_SMI_ONLY_ELEMENTS) {
+ UNIMPLEMENTED();
+ return Failure::Exception();
+ } else {
+ MaybeObject* maybe_value = from_obj->get(i + from_start);
+ Object* value;
+ ASSERT(to_kind == FAST_ELEMENTS);
+ // Because FAST_DOUBLE_ELEMENTS -> FAST_ELEMENT allocate HeapObjects
+ // iteratively, the allocate must succeed within a single GC cycle,
+ // otherwise the retry after the GC will also fail. In order to ensure
+ // that no GC is triggered, allocate HeapNumbers from old space if they
+ // can't be taken from new space.
+ if (!maybe_value->ToObject(&value)) {
+ ASSERT(maybe_value->IsRetryAfterGC() || maybe_value->IsOutOfMemory());
+ Heap* heap = from_obj->GetHeap();
+ MaybeObject* maybe_value_object =
+ heap->AllocateHeapNumber(from_obj->get_scalar(i + from_start),
+ TENURED);
+ if (!maybe_value_object->ToObject(&value)) return maybe_value_object;
+ }
+ to_obj->set(i + to_start, value, UPDATE_WRITE_BARRIER);
+ }
+ }
+ return to_obj;
+}
+
+
+static void CopyDoubleToDoubleElements(FixedDoubleArray* from_obj,
+ uint32_t from_start,
+ FixedDoubleArray* to_obj,
+ uint32_t to_start,
+ int copy_size) {
+ if (copy_size == -1) {
+ copy_size = Min(from_obj->length() - from_start,
+ to_obj->length() - to_start);
+ }
+ ASSERT(((copy_size + static_cast<int>(to_start)) <= to_obj->length() &&
+ (copy_size + static_cast<int>(from_start)) <= from_obj->length()));
+ if (copy_size == 0) return;
+ Address to = to_obj->address() + FixedDoubleArray::kHeaderSize;
+ Address from = from_obj->address() + FixedDoubleArray::kHeaderSize;
+ to += kDoubleSize * to_start;
+ from += kDoubleSize * from_start;
+ int words_per_double = (kDoubleSize / kPointerSize);
+ CopyWords(reinterpret_cast<Object**>(to),
+ reinterpret_cast<Object**>(from),
+ words_per_double * copy_size);
+}
+
+
// Base class for element handler implementations. Contains the
// the common logic for objects with different ElementsKinds.
// Subclasses must specialize method for which the element
@@ -101,15 +282,22 @@
// http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). We use
// CRTP to guarantee aggressive compile time optimizations (i.e. inlining and
// specialization of SomeElementsAccessor methods).
-template <typename ElementsAccessorSubclass, typename BackingStoreClass>
+template <typename ElementsAccessorSubclass,
+ typename ElementsTraitsParam>
class ElementsAccessorBase : public ElementsAccessor {
protected:
- explicit ElementsAccessorBase(const char* name) : ElementsAccessor(name) { }
+ explicit ElementsAccessorBase(const char* name)
+ : ElementsAccessor(name) { }
+
+ typedef ElementsTraitsParam ElementsTraits;
+ typedef typename ElementsTraitsParam::BackingStore BackingStore;
+
+ virtual ElementsKind kind() const { return ElementsTraits::Kind; }
static bool HasElementImpl(Object* receiver,
JSObject* holder,
uint32_t key,
- BackingStoreClass* backing_store) {
+ BackingStore* backing_store) {
MaybeObject* element =
ElementsAccessorSubclass::GetImpl(receiver, holder, key, backing_store);
return !element->IsTheHole();
@@ -123,7 +311,7 @@
backing_store = holder->elements();
}
return ElementsAccessorSubclass::HasElementImpl(
- receiver, holder, key, BackingStoreClass::cast(backing_store));
+ receiver, holder, key, BackingStore::cast(backing_store));
}
virtual MaybeObject* Get(Object* receiver,
@@ -134,13 +322,13 @@
backing_store = holder->elements();
}
return ElementsAccessorSubclass::GetImpl(
- receiver, holder, key, BackingStoreClass::cast(backing_store));
+ receiver, holder, key, BackingStore::cast(backing_store));
}
static MaybeObject* GetImpl(Object* receiver,
JSObject* obj,
uint32_t key,
- BackingStoreClass* backing_store) {
+ BackingStore* backing_store) {
return (key < ElementsAccessorSubclass::GetCapacityImpl(backing_store))
? backing_store->get(key)
: backing_store->GetHeap()->the_hole_value();
@@ -149,12 +337,12 @@
virtual MaybeObject* SetLength(JSArray* array,
Object* length) {
return ElementsAccessorSubclass::SetLengthImpl(
- array, length, BackingStoreClass::cast(array->elements()));
+ array, length, BackingStore::cast(array->elements()));
}
static MaybeObject* SetLengthImpl(JSObject* obj,
Object* length,
- BackingStoreClass* backing_store);
+ BackingStore* backing_store);
virtual MaybeObject* SetCapacityAndLength(JSArray* array,
int capacity,
@@ -176,6 +364,30 @@
uint32_t key,
JSReceiver::DeleteMode mode) = 0;
+ static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+ uint32_t from_start,
+ FixedArrayBase* to,
+ ElementsKind to_kind,
+ uint32_t to_start,
+ int copy_size) {
+ UNREACHABLE();
+ return NULL;
+ }
+
+ virtual MaybeObject* CopyElements(JSObject* from_holder,
+ uint32_t from_start,
+ FixedArrayBase* to,
+ ElementsKind to_kind,
+ uint32_t to_start,
+ int copy_size,
+ FixedArrayBase* from) {
+ if (from == NULL) {
+ from = from_holder->elements();
+ }
+ return ElementsAccessorSubclass::CopyElementsImpl(
+ from, from_start, to, to_kind, to_start, copy_size);
+ }
+
virtual MaybeObject* AddElementsToFixedArray(Object* receiver,
JSObject* holder,
FixedArray* to,
@@ -191,7 +403,7 @@
if (from == NULL) {
from = holder->elements();
}
- BackingStoreClass* backing_store = BackingStoreClass::cast(from);
+ BackingStore* backing_store = BackingStore::cast(from);
uint32_t len1 = ElementsAccessorSubclass::GetCapacityImpl(backing_store);
// Optimize if 'other' is empty.
@@ -258,16 +470,16 @@
}
protected:
- static uint32_t GetCapacityImpl(BackingStoreClass* backing_store) {
+ static uint32_t GetCapacityImpl(BackingStore* backing_store) {
return backing_store->length();
}
virtual uint32_t GetCapacity(FixedArrayBase* backing_store) {
return ElementsAccessorSubclass::GetCapacityImpl(
- BackingStoreClass::cast(backing_store));
+ BackingStore::cast(backing_store));
}
- static uint32_t GetKeyForIndexImpl(BackingStoreClass* backing_store,
+ static uint32_t GetKeyForIndexImpl(BackingStore* backing_store,
uint32_t index) {
return index;
}
@@ -275,7 +487,7 @@
virtual uint32_t GetKeyForIndex(FixedArrayBase* backing_store,
uint32_t index) {
return ElementsAccessorSubclass::GetKeyForIndexImpl(
- BackingStoreClass::cast(backing_store), index);
+ BackingStore::cast(backing_store), index);
}
private:
@@ -285,16 +497,18 @@
// Super class for all fast element arrays.
template<typename FastElementsAccessorSubclass,
- typename BackingStore,
+ typename KindTraits,
int ElementSize>
class FastElementsAccessor
- : public ElementsAccessorBase<FastElementsAccessorSubclass, BackingStore> {
+ : public ElementsAccessorBase<FastElementsAccessorSubclass, KindTraits> {
public:
explicit FastElementsAccessor(const char* name)
: ElementsAccessorBase<FastElementsAccessorSubclass,
- BackingStore>(name) {}
+ KindTraits>(name) {}
protected:
- friend class ElementsAccessorBase<FastElementsAccessorSubclass, BackingStore>;
+ friend class ElementsAccessorBase<FastElementsAccessorSubclass, KindTraits>;
+
+ typedef typename KindTraits::BackingStore BackingStore;
// Adjusts the length of the fast backing store or returns the new length or
// undefined in case conversion to a slow backing store should be performed.
@@ -349,12 +563,12 @@
class FastObjectElementsAccessor
: public FastElementsAccessor<FastObjectElementsAccessor,
- FixedArray,
+ ElementsKindTraits<FAST_ELEMENTS>,
kPointerSize> {
public:
explicit FastObjectElementsAccessor(const char* name)
: FastElementsAccessor<FastObjectElementsAccessor,
- FixedArray,
+ ElementsKindTraits<FAST_ELEMENTS>,
kPointerSize>(name) {}
static MaybeObject* DeleteCommon(JSObject* obj,
@@ -403,6 +617,28 @@
return heap->true_value();
}
+ static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+ uint32_t from_start,
+ FixedArrayBase* to,
+ ElementsKind to_kind,
+ uint32_t to_start,
+ int copy_size) {
+ switch (to_kind) {
+ case FAST_SMI_ONLY_ELEMENTS:
+ case FAST_ELEMENTS: {
+ AssertNoAllocation no_gc;
+ CopyObjectToObjectElements(
+ &no_gc, FixedArray::cast(from), ElementsTraits::Kind, from_start,
+ FixedArray::cast(to), to_kind, to_start, copy_size);
+ return from;
+ }
+ default:
+ UNREACHABLE();
+ }
+ return to->GetHeap()->undefined_value();
+ }
+
+
static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj,
uint32_t capacity,
uint32_t length) {
@@ -417,7 +653,7 @@
protected:
friend class FastElementsAccessor<FastObjectElementsAccessor,
- FixedArray,
+ ElementsKindTraits<FAST_ELEMENTS>,
kPointerSize>;
virtual MaybeObject* Delete(JSObject* obj,
@@ -430,12 +666,12 @@
class FastDoubleElementsAccessor
: public FastElementsAccessor<FastDoubleElementsAccessor,
- FixedDoubleArray,
+ ElementsKindTraits<FAST_DOUBLE_ELEMENTS>,
kDoubleSize> {
public:
explicit FastDoubleElementsAccessor(const char* name)
: FastElementsAccessor<FastDoubleElementsAccessor,
- FixedDoubleArray,
+ ElementsKindTraits<FAST_DOUBLE_ELEMENTS>,
kDoubleSize>(name) {}
static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj,
@@ -446,11 +682,34 @@
protected:
friend class ElementsAccessorBase<FastDoubleElementsAccessor,
- FixedDoubleArray>;
+ ElementsKindTraits<FAST_DOUBLE_ELEMENTS> >;
friend class FastElementsAccessor<FastDoubleElementsAccessor,
- FixedDoubleArray,
+ ElementsKindTraits<FAST_DOUBLE_ELEMENTS>,
kDoubleSize>;
+ static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+ uint32_t from_start,
+ FixedArrayBase* to,
+ ElementsKind to_kind,
+ uint32_t to_start,
+ int copy_size) {
+ switch (to_kind) {
+ case FAST_SMI_ONLY_ELEMENTS:
+ case FAST_ELEMENTS:
+ return CopyDoubleToObjectElements(
+ FixedDoubleArray::cast(from), from_start, FixedArray::cast(to),
+ to_kind, to_start, copy_size);
+ case FAST_DOUBLE_ELEMENTS:
+ CopyDoubleToDoubleElements(FixedDoubleArray::cast(from), from_start,
+ FixedDoubleArray::cast(to),
+ to_start, copy_size);
+ return from;
+ default:
+ UNREACHABLE();
+ }
+ return to->GetHeap()->undefined_value();
+ }
+
virtual MaybeObject* Delete(JSObject* obj,
uint32_t key,
JSReceiver::DeleteMode mode) {
@@ -474,23 +733,25 @@
// Super class for all external element arrays.
template<typename ExternalElementsAccessorSubclass,
- typename ExternalArray>
+ ElementsKind Kind>
class ExternalElementsAccessor
: public ElementsAccessorBase<ExternalElementsAccessorSubclass,
- ExternalArray> {
+ ElementsKindTraits<Kind> > {
public:
explicit ExternalElementsAccessor(const char* name)
: ElementsAccessorBase<ExternalElementsAccessorSubclass,
- ExternalArray>(name) {}
+ ElementsKindTraits<Kind> >(name) {}
protected:
+ typedef typename ElementsKindTraits<Kind>::BackingStore BackingStore;
+
friend class ElementsAccessorBase<ExternalElementsAccessorSubclass,
- ExternalArray>;
+ ElementsKindTraits<Kind> >;
static MaybeObject* GetImpl(Object* receiver,
JSObject* obj,
uint32_t key,
- ExternalArray* backing_store) {
+ BackingStore* backing_store) {
return
key < ExternalElementsAccessorSubclass::GetCapacityImpl(backing_store)
? backing_store->get(key)
@@ -499,7 +760,7 @@
static MaybeObject* SetLengthImpl(JSObject* obj,
Object* length,
- ExternalArray* backing_store) {
+ BackingStore* backing_store) {
// External arrays do not support changing their length.
UNREACHABLE();
return obj;
@@ -515,7 +776,7 @@
static bool HasElementImpl(Object* receiver,
JSObject* holder,
uint32_t key,
- ExternalArray* backing_store) {
+ BackingStore* backing_store) {
uint32_t capacity =
ExternalElementsAccessorSubclass::GetCapacityImpl(backing_store);
return key < capacity;
@@ -525,101 +786,101 @@
class ExternalByteElementsAccessor
: public ExternalElementsAccessor<ExternalByteElementsAccessor,
- ExternalByteArray> {
+ EXTERNAL_BYTE_ELEMENTS> {
public:
explicit ExternalByteElementsAccessor(const char* name)
: ExternalElementsAccessor<ExternalByteElementsAccessor,
- ExternalByteArray>(name) {}
+ EXTERNAL_BYTE_ELEMENTS>(name) {}
};
class ExternalUnsignedByteElementsAccessor
: public ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor,
- ExternalUnsignedByteArray> {
+ EXTERNAL_UNSIGNED_BYTE_ELEMENTS> {
public:
explicit ExternalUnsignedByteElementsAccessor(const char* name)
: ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor,
- ExternalUnsignedByteArray>(name) {}
+ EXTERNAL_UNSIGNED_BYTE_ELEMENTS>(name) {}
};
class ExternalShortElementsAccessor
: public ExternalElementsAccessor<ExternalShortElementsAccessor,
- ExternalShortArray> {
+ EXTERNAL_SHORT_ELEMENTS> {
public:
explicit ExternalShortElementsAccessor(const char* name)
: ExternalElementsAccessor<ExternalShortElementsAccessor,
- ExternalShortArray>(name) {}
+ EXTERNAL_SHORT_ELEMENTS>(name) {}
};
class ExternalUnsignedShortElementsAccessor
: public ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor,
- ExternalUnsignedShortArray> {
+ EXTERNAL_UNSIGNED_SHORT_ELEMENTS> {
public:
explicit ExternalUnsignedShortElementsAccessor(const char* name)
: ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor,
- ExternalUnsignedShortArray>(name) {}
+ EXTERNAL_UNSIGNED_SHORT_ELEMENTS>(name) {}
};
class ExternalIntElementsAccessor
: public ExternalElementsAccessor<ExternalIntElementsAccessor,
- ExternalIntArray> {
+ EXTERNAL_INT_ELEMENTS> {
public:
explicit ExternalIntElementsAccessor(const char* name)
: ExternalElementsAccessor<ExternalIntElementsAccessor,
- ExternalIntArray>(name) {}
+ EXTERNAL_INT_ELEMENTS>(name) {}
};
class ExternalUnsignedIntElementsAccessor
: public ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor,
- ExternalUnsignedIntArray> {
+ EXTERNAL_UNSIGNED_INT_ELEMENTS> {
public:
explicit ExternalUnsignedIntElementsAccessor(const char* name)
: ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor,
- ExternalUnsignedIntArray>(name) {}
+ EXTERNAL_UNSIGNED_INT_ELEMENTS>(name) {}
};
class ExternalFloatElementsAccessor
: public ExternalElementsAccessor<ExternalFloatElementsAccessor,
- ExternalFloatArray> {
+ EXTERNAL_FLOAT_ELEMENTS> {
public:
explicit ExternalFloatElementsAccessor(const char* name)
: ExternalElementsAccessor<ExternalFloatElementsAccessor,
- ExternalFloatArray>(name) {}
+ EXTERNAL_FLOAT_ELEMENTS>(name) {}
};
class ExternalDoubleElementsAccessor
: public ExternalElementsAccessor<ExternalDoubleElementsAccessor,
- ExternalDoubleArray> {
+ EXTERNAL_DOUBLE_ELEMENTS> {
public:
explicit ExternalDoubleElementsAccessor(const char* name)
: ExternalElementsAccessor<ExternalDoubleElementsAccessor,
- ExternalDoubleArray>(name) {}
+ EXTERNAL_DOUBLE_ELEMENTS>(name) {}
};
class PixelElementsAccessor
: public ExternalElementsAccessor<PixelElementsAccessor,
- ExternalPixelArray> {
+ EXTERNAL_PIXEL_ELEMENTS> {
public:
explicit PixelElementsAccessor(const char* name)
: ExternalElementsAccessor<PixelElementsAccessor,
- ExternalPixelArray>(name) {}
+ EXTERNAL_PIXEL_ELEMENTS>(name) {}
};
class DictionaryElementsAccessor
: public ElementsAccessorBase<DictionaryElementsAccessor,
- SeededNumberDictionary> {
+ ElementsKindTraits<DICTIONARY_ELEMENTS> > {
public:
explicit DictionaryElementsAccessor(const char* name)
: ElementsAccessorBase<DictionaryElementsAccessor,
- SeededNumberDictionary>(name) {}
+ ElementsKindTraits<DICTIONARY_ELEMENTS> >(name) {}
// Adjusts the length of the dictionary backing store and returns the new
// length according to ES5 section 15.4.5.2 behavior.
@@ -723,9 +984,29 @@
return heap->true_value();
}
+ static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+ uint32_t from_start,
+ FixedArrayBase* to,
+ ElementsKind to_kind,
+ uint32_t to_start,
+ int copy_size) {
+ switch (to_kind) {
+ case FAST_SMI_ONLY_ELEMENTS:
+ case FAST_ELEMENTS:
+ CopyDictionaryToObjectElements(
+ SeededNumberDictionary::cast(from), from_start,
+ FixedArray::cast(to), to_kind, to_start, copy_size);
+ return from;
+ default:
+ UNREACHABLE();
+ }
+ return to->GetHeap()->undefined_value();
+ }
+
+
protected:
friend class ElementsAccessorBase<DictionaryElementsAccessor,
- SeededNumberDictionary>;
+ ElementsKindTraits<DICTIONARY_ELEMENTS> >;
virtual MaybeObject* Delete(JSObject* obj,
uint32_t key,
@@ -769,16 +1050,18 @@
};
-class NonStrictArgumentsElementsAccessor
- : public ElementsAccessorBase<NonStrictArgumentsElementsAccessor,
- FixedArray> {
+class NonStrictArgumentsElementsAccessor : public ElementsAccessorBase<
+ NonStrictArgumentsElementsAccessor,
+ ElementsKindTraits<NON_STRICT_ARGUMENTS_ELEMENTS> > {
public:
explicit NonStrictArgumentsElementsAccessor(const char* name)
- : ElementsAccessorBase<NonStrictArgumentsElementsAccessor,
- FixedArray>(name) {}
+ : ElementsAccessorBase<
+ NonStrictArgumentsElementsAccessor,
+ ElementsKindTraits<NON_STRICT_ARGUMENTS_ELEMENTS> >(name) {}
protected:
- friend class ElementsAccessorBase<NonStrictArgumentsElementsAccessor,
- FixedArray>;
+ friend class ElementsAccessorBase<
+ NonStrictArgumentsElementsAccessor,
+ ElementsKindTraits<NON_STRICT_ARGUMENTS_ELEMENTS> >;
static MaybeObject* GetImpl(Object* receiver,
JSObject* obj,
@@ -840,6 +1123,19 @@
return obj->GetHeap()->true_value();
}
+ static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+ uint32_t from_start,
+ FixedArrayBase* to,
+ ElementsKind to_kind,
+ uint32_t to_start,
+ int copy_size) {
+ FixedArray* parameter_map = FixedArray::cast(from);
+ FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
+ ElementsAccessor* accessor = ElementsAccessor::ForArray(arguments);
+ return accessor->CopyElements(NULL, from_start, to, to_kind,
+ to_start, copy_size, arguments);
+ }
+
static uint32_t GetCapacityImpl(FixedArray* parameter_map) {
FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1));
return Max(static_cast<uint32_t>(parameter_map->length() - 2),
@@ -913,45 +1209,22 @@
void ElementsAccessor::InitializeOncePerProcess() {
- // First argument in list is the accessor class, the second argument is can
- // be any arbitrary unique identifier, in this case chosen to be the
- // corresponding enum. Use the fast element handler for smi-only arrays.
- // The implementation is currently identical. Note that the order must match
- // that of the ElementsKind enum for the |accessor_array[]| below to work.
-#define ELEMENTS_LIST(V) \
- V(FastObjectElementsAccessor, FAST_SMI_ONLY_ELEMENTS) \
- V(FastObjectElementsAccessor, FAST_ELEMENTS) \
- V(FastDoubleElementsAccessor, FAST_DOUBLE_ELEMENTS) \
- V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS) \
- V(NonStrictArgumentsElementsAccessor, NON_STRICT_ARGUMENTS_ELEMENTS) \
- V(ExternalByteElementsAccessor, EXTERNAL_BYTE_ELEMENTS) \
- V(ExternalUnsignedByteElementsAccessor, EXTERNAL_UNSIGNED_BYTE_ELEMENTS) \
- V(ExternalShortElementsAccessor, EXTERNAL_SHORT_ELEMENTS) \
- V(ExternalUnsignedShortElementsAccessor, EXTERNAL_UNSIGNED_SHORT_ELEMENTS) \
- V(ExternalIntElementsAccessor, EXTERNAL_INT_ELEMENTS) \
- V(ExternalUnsignedIntElementsAccessor, EXTERNAL_UNSIGNED_INT_ELEMENTS) \
- V(ExternalFloatElementsAccessor, EXTERNAL_FLOAT_ELEMENTS) \
- V(ExternalDoubleElementsAccessor, EXTERNAL_DOUBLE_ELEMENTS) \
- V(PixelElementsAccessor, EXTERNAL_PIXEL_ELEMENTS)
-
static struct ConcreteElementsAccessors {
-#define ACCESSOR_STRUCT(Class, Name) Class* Name##_handler;
+#define ACCESSOR_STRUCT(Class, Kind, Store) Class* Kind##_handler;
ELEMENTS_LIST(ACCESSOR_STRUCT)
#undef ACCESSOR_STRUCT
} element_accessors = {
-#define ACCESSOR_INIT(Class, Name) new Class(#Name),
+#define ACCESSOR_INIT(Class, Kind, Store) new Class(#Kind),
ELEMENTS_LIST(ACCESSOR_INIT)
#undef ACCESSOR_INIT
};
static ElementsAccessor* accessor_array[] = {
-#define ACCESSOR_ARRAY(Class, Name) element_accessors.Name##_handler,
+#define ACCESSOR_ARRAY(Class, Kind, Store) element_accessors.Kind##_handler,
ELEMENTS_LIST(ACCESSOR_ARRAY)
#undef ACCESSOR_ARRAY
};
-#undef ELEMENTS_LIST
-
STATIC_ASSERT((sizeof(accessor_array) / sizeof(*accessor_array)) ==
kElementsKindCount);
@@ -959,11 +1232,12 @@
}
-template <typename ElementsAccessorSubclass, typename BackingStoreClass>
-MaybeObject* ElementsAccessorBase<ElementsAccessorSubclass, BackingStoreClass>::
+template <typename ElementsAccessorSubclass, typename ElementsKindTraits>
+MaybeObject* ElementsAccessorBase<ElementsAccessorSubclass,
+ ElementsKindTraits>::
SetLengthImpl(JSObject* obj,
Object* length,
- BackingStoreClass* backing_store) {
+ typename ElementsKindTraits::BackingStore* backing_store) {
JSArray* array = JSArray::cast(obj);
// Fast case: The new length fits into a Smi.
diff --git a/src/elements.h b/src/elements.h
index 036df5f..5b5be23 100644
--- a/src/elements.h
+++ b/src/elements.h
@@ -29,6 +29,8 @@
#define V8_ELEMENTS_H_
#include "objects.h"
+#include "heap.h"
+#include "isolate.h"
namespace v8 {
namespace internal {
@@ -40,7 +42,8 @@
explicit ElementsAccessor(const char* name) : name_(name) { }
virtual ~ElementsAccessor() { }
- virtual const char* name() const { return name_; }
+ virtual ElementsKind kind() const = 0;
+ const char* name() const { return name_; }
// Returns true if a holder contains an element with the specified key
// without iterating up the prototype chain. The caller can optionally pass
@@ -85,6 +88,25 @@
uint32_t key,
JSReceiver::DeleteMode mode) = 0;
+ // Copy elements from one backing store to another. Typically, callers specify
+ // the source JSObject or JSArray in source_holder. If the holder's backing
+ // store is available, it can be passed in source and source_holder is
+ // ignored.
+ virtual MaybeObject* CopyElements(JSObject* source_holder,
+ uint32_t source_start,
+ FixedArrayBase* destination,
+ ElementsKind destination_kind,
+ uint32_t destination_start,
+ int copy_size,
+ FixedArrayBase* source = NULL) = 0;
+
+ MaybeObject* CopyElements(JSObject* from_holder,
+ FixedArrayBase* to,
+ ElementsKind to_kind,
+ FixedArrayBase* from = NULL) {
+ return CopyElements(from_holder, 0, to, to_kind, 0, -1, from);
+ }
+
virtual MaybeObject* AddElementsToFixedArray(Object* receiver,
JSObject* holder,
FixedArray* to,
@@ -123,6 +145,17 @@
DISALLOW_COPY_AND_ASSIGN(ElementsAccessor);
};
+
+void CopyObjectToObjectElements(AssertNoAllocation* no_gc,
+ FixedArray* from_obj,
+ ElementsKind from_kind,
+ uint32_t from_start,
+ FixedArray* to_obj,
+ ElementsKind to_kind,
+ uint32_t to_start,
+ int copy_size);
+
+
} } // namespace v8::internal
#endif // V8_ELEMENTS_H_
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index c62e18d..ac30b29 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -177,22 +177,28 @@
// Experimental profiler changes.
DEFINE_bool(experimental_profiler, false, "enable all profiler experiments")
DEFINE_bool(watch_ic_patching, false, "profiler considers IC stability")
-DEFINE_int(frame_count, 2, "number of stack frames inspected by the profiler")
+DEFINE_int(frame_count, 1, "number of stack frames inspected by the profiler")
DEFINE_bool(self_optimization, false,
"primitive functions trigger their own optimization")
+DEFINE_bool(direct_self_opt, false,
+ "call recompile stub directly when self-optimizing")
+DEFINE_bool(retry_self_opt, false, "re-try self-optimization if it failed")
DEFINE_bool(count_based_interrupts, false,
"trigger profiler ticks based on counting instead of timing")
DEFINE_bool(interrupt_at_exit, false,
"insert an interrupt check at function exit")
DEFINE_bool(weighted_back_edges, false,
"weight back edges by jump distance for interrupt triggering")
-DEFINE_int(interrupt_budget, 10000,
+DEFINE_int(interrupt_budget, 5900,
"execution budget before interrupt is triggered")
-DEFINE_int(type_info_threshold, 0,
+DEFINE_int(type_info_threshold, 40,
"percentage of ICs that must have type info to allow optimization")
+DEFINE_int(self_opt_count, 130, "call count before self-optimization")
DEFINE_implication(experimental_profiler, watch_ic_patching)
DEFINE_implication(experimental_profiler, self_optimization)
+// Not implying direct_self_opt here because it seems to be a bad idea.
+DEFINE_implication(experimental_profiler, retry_self_opt)
DEFINE_implication(experimental_profiler, count_based_interrupts)
DEFINE_implication(experimental_profiler, interrupt_at_exit)
DEFINE_implication(experimental_profiler, weighted_back_edges)
diff --git a/src/full-codegen.h b/src/full-codegen.h
index 0ffb885..25e3dba 100644
--- a/src/full-codegen.h
+++ b/src/full-codegen.h
@@ -437,6 +437,9 @@
// the offset of the start of the table.
unsigned EmitStackCheckTable();
+ void EmitProfilingCounterDecrement(int delta);
+ void EmitProfilingCounterReset();
+
// Platform-specific return sequence
void EmitReturnSequence();
diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h
index a05fc77..5733e51 100644
--- a/src/hydrogen-instructions.h
+++ b/src/hydrogen-instructions.h
@@ -184,7 +184,8 @@
V(ForInPrepareMap) \
V(ForInCacheArray) \
V(CheckMapValue) \
- V(LoadFieldByIndex)
+ V(LoadFieldByIndex) \
+ V(DateField)
#define GVN_FLAG_LIST(V) \
V(Calls) \
@@ -4602,6 +4603,26 @@
};
+class HDateField: public HUnaryOperation {
+ public:
+ HDateField(HValue* date, Smi* index)
+ : HUnaryOperation(date), index_(index) {
+ set_representation(Representation::Tagged());
+ }
+
+ Smi* index() const { return index_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(DateField)
+
+ private:
+ Smi* index_;
+};
+
+
class HDeleteProperty: public HBinaryOperation {
public:
HDeleteProperty(HValue* context, HValue* obj, HValue* key)
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index fe23a14..11b1157 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -7122,6 +7122,17 @@
}
+void HGraphBuilder::GenerateDateField(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 2);
+ ASSERT_NE(NULL, call->arguments()->at(1)->AsLiteral());
+ 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);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
void HGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
ASSERT(call->arguments()->length() == 2);
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc
index 54a1a34..d3e2a91 100644
--- a/src/ia32/code-stubs-ia32.cc
+++ b/src/ia32/code-stubs-ia32.cc
@@ -6462,7 +6462,7 @@
__ mov_b(scratch, Operand(left, index, times_1, 0));
__ cmpb(scratch, Operand(right, index, times_1, 0));
__ j(not_equal, chars_not_equal, chars_not_equal_near);
- __ add(index, Immediate(1));
+ __ inc(index);
__ j(not_zero, &loop);
}
@@ -6645,9 +6645,10 @@
void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::STRINGS);
- ASSERT(GetCondition() == equal);
Label miss;
+ bool equality = Token::IsEqualityOp(op_);
+
// Registers containing left and right operands respectively.
Register left = edx;
Register right = eax;
@@ -6686,25 +6687,33 @@
__ bind(¬_same);
// Check that both strings are symbols. If they are, we're done
- // because we already know they are not identical.
- Label do_compare;
- STATIC_ASSERT(kSymbolTag != 0);
- __ and_(tmp1, tmp2);
- __ test(tmp1, Immediate(kIsSymbolMask));
- __ j(zero, &do_compare, Label::kNear);
- // Make sure eax is non-zero. At this point input operands are
- // guaranteed to be non-zero.
- ASSERT(right.is(eax));
- __ ret(0);
+ // because we already know they are not identical. But in the case of
+ // non-equality compare, we still need to determine the order.
+ if (equality) {
+ Label do_compare;
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ and_(tmp1, tmp2);
+ __ test(tmp1, Immediate(kIsSymbolMask));
+ __ j(zero, &do_compare, Label::kNear);
+ // Make sure eax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(eax));
+ __ ret(0);
+ __ bind(&do_compare);
+ }
// Check that both strings are sequential ASCII.
Label runtime;
- __ bind(&do_compare);
__ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
// Compare flat ASCII strings. Returns when done.
- StringCompareStub::GenerateFlatAsciiStringEquals(
- masm, left, right, tmp1, tmp2);
+ if (equality) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(
+ masm, left, right, tmp1, tmp2);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(
+ masm, left, right, tmp1, tmp2, tmp3);
+ }
// Handle more complex cases in runtime.
__ bind(&runtime);
@@ -6712,7 +6721,11 @@
__ push(left);
__ push(right);
__ push(tmp1);
- __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+ if (equality) {
+ __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+ } else {
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+ }
__ bind(&miss);
GenerateMiss(masm);
diff --git a/src/ia32/deoptimizer-ia32.cc b/src/ia32/deoptimizer-ia32.cc
index 67291c3..11de1c4 100644
--- a/src/ia32/deoptimizer-ia32.cc
+++ b/src/ia32/deoptimizer-ia32.cc
@@ -206,7 +206,7 @@
static const byte kJnsInstruction = 0x79;
-static const byte kJnsOffset = 0x11;
+static const byte kJnsOffset = 0x13;
static const byte kJaeInstruction = 0x73;
static const byte kJaeOffset = 0x07;
static const byte kCallInstruction = 0xe8;
@@ -219,8 +219,8 @@
Code* check_code,
Code* replacement_code) {
Address call_target_address = pc_after - kIntSize;
- ASSERT(check_code->entry() ==
- Assembler::target_address_at(call_target_address));
+ ASSERT_EQ(check_code->entry(),
+ Assembler::target_address_at(call_target_address));
// The stack check code matches the pattern:
//
// cmp esp, <limit>
@@ -239,13 +239,13 @@
// ok:
if (FLAG_count_based_interrupts) {
- ASSERT(*(call_target_address - 3) == kJnsInstruction);
- ASSERT(*(call_target_address - 2) == kJnsOffset);
+ ASSERT_EQ(*(call_target_address - 3), kJnsInstruction);
+ ASSERT_EQ(*(call_target_address - 2), kJnsOffset);
} else {
- ASSERT(*(call_target_address - 3) == kJaeInstruction);
- ASSERT(*(call_target_address - 2) == kJaeOffset);
+ ASSERT_EQ(*(call_target_address - 3), kJaeInstruction);
+ ASSERT_EQ(*(call_target_address - 2), kJaeOffset);
}
- ASSERT(*(call_target_address - 1) == kCallInstruction);
+ ASSERT_EQ(*(call_target_address - 1), kCallInstruction);
*(call_target_address - 3) = kNopByteOne;
*(call_target_address - 2) = kNopByteTwo;
Assembler::set_target_address_at(call_target_address,
@@ -261,14 +261,14 @@
Code* check_code,
Code* replacement_code) {
Address call_target_address = pc_after - kIntSize;
- ASSERT(replacement_code->entry() ==
- Assembler::target_address_at(call_target_address));
+ ASSERT_EQ(replacement_code->entry(),
+ Assembler::target_address_at(call_target_address));
// Replace the nops from patching (Deoptimizer::PatchStackCheckCode) to
// restore the conditional branch.
- ASSERT(*(call_target_address - 3) == kNopByteOne &&
- *(call_target_address - 2) == kNopByteTwo &&
- *(call_target_address - 1) == kCallInstruction);
+ ASSERT_EQ(*(call_target_address - 3), kNopByteOne);
+ ASSERT_EQ(*(call_target_address - 2), kNopByteTwo);
+ ASSERT_EQ(*(call_target_address - 1), kCallInstruction);
if (FLAG_count_based_interrupts) {
*(call_target_address - 3) = kJnsInstruction;
*(call_target_address - 2) = kJnsOffset;
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index b55f428..b42ce95 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -127,28 +127,6 @@
SetFunctionPosition(function());
Comment cmnt(masm_, "[ function compiled by full code generator");
- // We can optionally optimize based on counters rather than statistical
- // sampling.
- if (info->ShouldSelfOptimize()) {
- if (FLAG_trace_opt_verbose) {
- PrintF("[adding self-optimization header to %s]\n",
- *info->function()->debug_name()->ToCString());
- }
- has_self_optimization_header_ = true;
- MaybeObject* maybe_cell = isolate()->heap()->AllocateJSGlobalPropertyCell(
- Smi::FromInt(Compiler::kCallsUntilPrimitiveOpt));
- JSGlobalPropertyCell* cell;
- if (maybe_cell->To(&cell)) {
- __ sub(Operand::Cell(Handle<JSGlobalPropertyCell>(cell)),
- Immediate(Smi::FromInt(1)));
- Handle<Code> compile_stub(
- isolate()->builtins()->builtin(Builtins::kLazyRecompile));
- STATIC_ASSERT(kSmiTag == 0);
- __ j(zero, compile_stub);
- ASSERT(masm_->pc_offset() == self_optimization_header_size());
- }
- }
-
#ifdef DEBUG
if (strlen(FLAG_stop_at) > 0 &&
info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
@@ -330,6 +308,25 @@
}
+void FullCodeGenerator::EmitProfilingCounterDecrement(int delta) {
+ __ mov(ebx, Immediate(profiling_counter_));
+ __ sub(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
+ Immediate(Smi::FromInt(delta)));
+}
+
+
+void FullCodeGenerator::EmitProfilingCounterReset() {
+ int reset_value = FLAG_interrupt_budget;
+ if (info_->ShouldSelfOptimize() && !FLAG_retry_self_opt) {
+ // Self-optimization is a one-off thing: if it fails, don't try again.
+ reset_value = Smi::kMaxValue;
+ }
+ __ mov(ebx, Immediate(profiling_counter_));
+ __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
+ Immediate(Smi::FromInt(reset_value)));
+}
+
+
void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt,
Label* back_edge_target) {
Comment cmnt(masm_, "[ Stack check");
@@ -342,15 +339,7 @@
int distance = masm_->SizeOfCodeGeneratedSince(back_edge_target);
weight = Min(127, Max(1, distance / 100));
}
- if (Serializer::enabled()) {
- __ mov(ebx, Immediate(profiling_counter_));
- __ sub(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
- Immediate(Smi::FromInt(weight)));
- } else {
- // This version is slightly faster, but not snapshot safe.
- __ sub(Operand::Cell(profiling_counter_),
- Immediate(Smi::FromInt(weight)));
- }
+ EmitProfilingCounterDecrement(weight);
__ j(positive, &ok, Label::kNear);
InterruptStub stub;
__ CallStub(&stub);
@@ -379,15 +368,7 @@
__ test(eax, Immediate(Min(loop_depth(), Code::kMaxLoopNestingMarker)));
if (FLAG_count_based_interrupts) {
- // Reset the countdown.
- if (Serializer::enabled()) {
- __ mov(ebx, Immediate(profiling_counter_));
- __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
- Immediate(Smi::FromInt(FLAG_interrupt_budget)));
- } else {
- __ mov(Operand::Cell(profiling_counter_),
- Immediate(Smi::FromInt(FLAG_interrupt_budget)));
- }
+ EmitProfilingCounterReset();
}
__ bind(&ok);
@@ -410,37 +391,28 @@
__ push(eax);
__ CallRuntime(Runtime::kTraceExit, 1);
}
- if (FLAG_interrupt_at_exit) {
+ if (FLAG_interrupt_at_exit || FLAG_self_optimization) {
// Pretend that the exit is a backwards jump to the entry.
int weight = 1;
- if (FLAG_weighted_back_edges) {
+ if (info_->ShouldSelfOptimize()) {
+ weight = FLAG_interrupt_budget / FLAG_self_opt_count;
+ } else if (FLAG_weighted_back_edges) {
int distance = masm_->pc_offset();
weight = Min(127, Max(1, distance / 100));
}
- if (Serializer::enabled()) {
- __ mov(ebx, Immediate(profiling_counter_));
- __ sub(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
- Immediate(Smi::FromInt(weight)));
- } else {
- // This version is slightly faster, but not snapshot safe.
- __ sub(Operand::Cell(profiling_counter_),
- Immediate(Smi::FromInt(weight)));
- }
+ EmitProfilingCounterDecrement(weight);
Label ok;
__ j(positive, &ok, Label::kNear);
__ push(eax);
- InterruptStub stub;
- __ CallStub(&stub);
- __ pop(eax);
- // Reset the countdown.
- if (Serializer::enabled()) {
- __ mov(ebx, Immediate(profiling_counter_));
- __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
- Immediate(Smi::FromInt(FLAG_interrupt_budget)));
+ if (info_->ShouldSelfOptimize() && FLAG_direct_self_opt) {
+ __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ __ CallRuntime(Runtime::kOptimizeFunctionOnNextCall, 1);
} else {
- __ mov(Operand::Cell(profiling_counter_),
- Immediate(Smi::FromInt(FLAG_interrupt_budget)));
+ InterruptStub stub;
+ __ CallStub(&stub);
}
+ __ pop(eax);
+ EmitProfilingCounterReset();
__ bind(&ok);
}
#ifdef DEBUG
@@ -2961,6 +2933,48 @@
}
+void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+ ASSERT_NE(NULL, args->at(1)->AsLiteral());
+ 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(object);
+ __ CmpObjectType(object, JS_DATE_TYPE, scratch);
+ __ Assert(equal, "Trying to get date field from non-date.");
+#endif
+
+ 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);
+}
+
+
void FullCodeGenerator::EmitMathPow(CallRuntime* expr) {
// Load the arguments on the stack and call the runtime function.
ZoneList<Expression*>* args = expr->arguments();
diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc
index 3d953c3..a656175 100644
--- a/src/ia32/lithium-codegen-ia32.cc
+++ b/src/ia32/lithium-codegen-ia32.cc
@@ -1273,6 +1273,7 @@
Register result = ToRegister(instr->result());
Register map = ToRegister(instr->TempAt(0));
ASSERT(input.is(result));
+
Label done;
// If the object is a smi return the object.
__ JumpIfSmi(input, &done, Label::kNear);
@@ -1286,6 +1287,43 @@
}
+void LCodeGen::DoDateField(LDateField* instr) {
+ Register object = ToRegister(instr->InputAt(0));
+ Register result = ToRegister(instr->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(object);
+ __ CmpObjectType(object, JS_DATE_TYPE, scratch);
+ __ Assert(equal, "Trying to get date field from non-date.");
+#endif
+
+ 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);
+ }
+}
+
+
void LCodeGen::DoBitNotI(LBitNotI* instr) {
LOperand* input = instr->InputAt(0);
ASSERT(input->Equals(instr->result()));
diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc
index 7587016..223fde2 100644
--- a/src/ia32/lithium-ia32.cc
+++ b/src/ia32/lithium-ia32.cc
@@ -1647,6 +1647,14 @@
}
+LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
+ LOperand* date = UseFixed(instr->value(), eax);
+ LDateField* result =
+ new(zone()) LDateField(date, FixedTemp(ecx), instr->index());
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) {
return AssignEnvironment(new(zone()) LBoundsCheck(
UseRegisterOrConstantAtStart(instr->index()),
diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h
index bf8dbc6..dd41bfb 100644
--- a/src/ia32/lithium-ia32.h
+++ b/src/ia32/lithium-ia32.h
@@ -172,7 +172,8 @@
V(ForInPrepareMap) \
V(ForInCacheArray) \
V(CheckMapValue) \
- V(LoadFieldByIndex)
+ V(LoadFieldByIndex) \
+ V(DateField)
#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
@@ -1002,6 +1003,24 @@
};
+class LDateField: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LDateField(LOperand* date, LOperand* temp, Smi* index)
+ : index_(index) {
+ inputs_[0] = date;
+ temps_[0] = temp;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(DateField, "date-field")
+ DECLARE_HYDROGEN_ACCESSOR(DateField)
+
+ Smi* index() const { return index_; }
+
+ private:
+ Smi* index_;
+};
+
+
class LThrow: public LTemplateInstruction<0, 2, 0> {
public:
LThrow(LOperand* context, LOperand* value) {
diff --git a/src/ic.cc b/src/ic.cc
index 88d8a84..c2ee45e 100644
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -2490,9 +2490,13 @@
return HEAP_NUMBERS;
}
}
- if (!Token::IsEqualityOp(op_)) return GENERIC;
- if (x->IsSymbol() && y->IsSymbol()) return SYMBOLS;
+ if (x->IsSymbol() && y->IsSymbol()) {
+ // We compare symbols as strings if we need to determine
+ // the order in a non-equality compare.
+ return Token::IsEqualityOp(op_) ? SYMBOLS : STRINGS;
+ }
if (x->IsString() && y->IsString()) return STRINGS;
+ if (!Token::IsEqualityOp(op_)) return GENERIC;
if (x->IsJSObject() && y->IsJSObject()) {
if (Handle<JSObject>::cast(x)->map() ==
Handle<JSObject>::cast(y)->map() &&
diff --git a/src/isolate.cc b/src/isolate.cc
index 93742bd..3dfcbb5 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -1486,6 +1486,7 @@
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 @@
delete unicode_cache_;
unicode_cache_ = NULL;
+ delete date_cache_;
+ date_cache_ = NULL;
+
delete regexp_stack_;
regexp_stack_ = NULL;
@@ -1782,6 +1786,7 @@
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();
diff --git a/src/isolate.h b/src/isolate.h
index 71fe883..2c2618a 100644
--- a/src/isolate.h
+++ b/src/isolate.h
@@ -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 @@
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 @@
unibrow::Mapping<unibrow::Ecma262Canonicalize>
regexp_macro_assembler_canonicalize_;
RegExpStack* regexp_stack_;
+
+ DateCache* date_cache_;
+
unibrow::Mapping<unibrow::Ecma262Canonicalize> interp_canonicalize_mapping_;
void* embedder_data_;
diff --git a/src/macros.py b/src/macros.py
index 8e9c62d..93287ae 100644
--- a/src/macros.py
+++ b/src/macros.py
@@ -164,16 +164,36 @@
# Gets the value of a Date object. If arg is not a Date object
# a type error is thrown.
-macro DATE_VALUE(arg) = (%_ClassOf(arg) === 'Date' ? %_ValueOf(arg) : ThrowDateTypeError());
-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]);
diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc
index e259fc4..c5ef2cc 100644
--- a/src/mips/full-codegen-mips.cc
+++ b/src/mips/full-codegen-mips.cc
@@ -1015,6 +1015,16 @@
// We got a fixed array in register v0. Iterate through that.
Label non_proxy;
__ bind(&fixed_array);
+
+ Handle<JSGlobalPropertyCell> cell =
+ isolate()->factory()->NewJSGlobalPropertyCell(
+ Handle<Object>(
+ Smi::FromInt(TypeFeedbackCells::kForInFastCaseMarker)));
+ RecordTypeFeedbackCell(stmt->PrepareId(), cell);
+ __ LoadHeapObject(a1, cell);
+ __ li(a2, Operand(Smi::FromInt(TypeFeedbackCells::kForInSlowCaseMarker)));
+ __ sw(a2, FieldMemOperand(a1, JSGlobalPropertyCell::kValueOffset));
+
__ li(a1, Operand(Smi::FromInt(1))); // Smi indicates slow check
__ lw(a2, MemOperand(sp, 0 * kPointerSize)); // Get enumerated object
STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index 7aef912..16e03e7 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -138,6 +138,9 @@
case JS_VALUE_TYPE:
JSValue::cast(this)->JSValueVerify();
break;
+ case JS_DATE_TYPE:
+ JSDate::cast(this)->JSDateVerify();
+ break;
case JS_FUNCTION_TYPE:
JSFunction::cast(this)->JSFunctionVerify();
break;
@@ -371,6 +374,53 @@
}
+void JSDate::JSDateVerify() {
+ if (value()->IsHeapObject()) {
+ VerifyHeapPointer(value());
+ }
+ CHECK(value()->IsUndefined() || value()->IsSmi() || value()->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(cache_stamp()->IsUndefined() ||
+ cache_stamp()->IsSmi() ||
+ cache_stamp()->IsNaN());
+
+ if (month()->IsSmi()) {
+ int month = Smi::cast(this->month())->value();
+ CHECK(0 <= month && month <= 11);
+ }
+ if (day()->IsSmi()) {
+ int day = Smi::cast(this->day())->value();
+ CHECK(1 <= day && day <= 31);
+ }
+ if (hour()->IsSmi()) {
+ int hour = Smi::cast(this->hour())->value();
+ CHECK(0 <= hour && hour <= 23);
+ }
+ if (min()->IsSmi()) {
+ int min = Smi::cast(this->min())->value();
+ CHECK(0 <= min && min <= 59);
+ }
+ if (sec()->IsSmi()) {
+ int sec = Smi::cast(this->sec())->value();
+ CHECK(0 <= sec && sec <= 59);
+ }
+ if (weekday()->IsSmi()) {
+ 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());
+ }
+}
+
+
void JSMessageObject::JSMessageObjectVerify() {
CHECK(IsJSMessageObject());
CHECK(type()->IsString());
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 176be9b..d0e9bf8 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -605,6 +605,7 @@
TYPE_CHECKER(JSGlobalPropertyCell, JS_GLOBAL_PROPERTY_CELL_TYPE)
TYPE_CHECKER(SharedFunctionInfo, SHARED_FUNCTION_INFO_TYPE)
TYPE_CHECKER(JSValue, JS_VALUE_TYPE)
+TYPE_CHECKER(JSDate, JS_DATE_TYPE)
TYPE_CHECKER(JSMessageObject, JS_MESSAGE_OBJECT_TYPE)
@@ -800,6 +801,11 @@
}
+bool Object::IsNaN() {
+ return this->IsHeapNumber() && isnan(HeapNumber::cast(this)->value());
+}
+
+
MaybeObject* Object::ToSmi() {
if (IsSmi()) return this;
if (IsHeapNumber()) {
@@ -1425,6 +1431,8 @@
return JSFunction::kSize;
case JS_VALUE_TYPE:
return JSValue::kSize;
+ case JS_DATE_TYPE:
+ return JSDate::kSize;
case JS_ARRAY_TYPE:
return JSArray::kSize;
case JS_WEAK_MAP_TYPE:
@@ -4119,6 +4127,24 @@
}
+ACCESSORS(JSDate, value, Object, kValueOffset)
+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)
+
+
+JSDate* JSDate::cast(Object* obj) {
+ ASSERT(obj->IsJSDate());
+ ASSERT(HeapObject::cast(obj)->Size() == JSDate::kSize);
+ return reinterpret_cast<JSDate*>(obj);
+}
+
+
ACCESSORS(JSMessageObject, type, String, kTypeOffset)
ACCESSORS(JSMessageObject, arguments, JSArray, kArgumentsOffset)
ACCESSORS(JSMessageObject, script, Object, kScriptOffset)
diff --git a/src/objects-printer.cc b/src/objects-printer.cc
index d5c02f4..38e6138 100644
--- a/src/objects-printer.cc
+++ b/src/objects-printer.cc
@@ -151,6 +151,9 @@
PrintF(out, "Value wrapper around:");
JSValue::cast(this)->value()->Print(out);
break;
+ case JS_DATE_TYPE:
+ JSDate::cast(this)->value()->Print(out);
+ break;
case CODE_TYPE:
Code::cast(this)->CodePrint(out);
break;
@@ -660,6 +663,30 @@
}
+static const char* const weekdays[] = {
+ "???", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+void JSDate::JSDatePrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSDate");
+ PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - value = ");
+ value()->Print(out);
+ if (!year()->IsSmi()) {
+ PrintF(out, " - time = NaN\n");
+ } else {
+ PrintF(out, " - time = %s %04d/%02d/%02d %02d:%02d:%02d\n",
+ weekdays[weekday()->IsSmi() ? Smi::cast(weekday())->value() + 1 : 0],
+ year()->IsSmi() ? Smi::cast(year())->value() : -1,
+ month()->IsSmi() ? Smi::cast(month())->value() : -1,
+ day()->IsSmi() ? Smi::cast(day())->value() : -1,
+ hour()->IsSmi() ? Smi::cast(hour())->value() : -1,
+ min()->IsSmi() ? Smi::cast(min())->value() : -1,
+ sec()->IsSmi() ? Smi::cast(sec())->value() : -1);
+ }
+}
+
+
void JSProxy::JSProxyPrint(FILE* out) {
HeapObject::PrintHeader(out, "JSProxy");
PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
diff --git a/src/objects-visiting.cc b/src/objects-visiting.cc
index 9ca102b..c7c8a87 100644
--- a/src/objects-visiting.cc
+++ b/src/objects-visiting.cc
@@ -134,6 +134,7 @@
case JS_OBJECT_TYPE:
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_VALUE_TYPE:
+ case JS_DATE_TYPE:
case JS_ARRAY_TYPE:
case JS_GLOBAL_PROXY_TYPE:
case JS_GLOBAL_OBJECT_TYPE:
diff --git a/src/objects.cc b/src/objects.cc
index bb2f2d3..e0a9537 100644
--- a/src/objects.cc
+++ b/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"
@@ -1067,7 +1068,7 @@
switch (map()->instance_type()) {
case JS_ARRAY_TYPE: {
double length = JSArray::cast(this)->length()->Number();
- accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
+ accumulator->Add("<JS Array[%u]>", static_cast<uint32_t>(length));
break;
}
case JS_WEAK_MAP_TYPE: {
@@ -1338,6 +1339,7 @@
case JS_OBJECT_TYPE:
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_VALUE_TYPE:
+ case JS_DATE_TYPE:
case JS_ARRAY_TYPE:
case JS_SET_TYPE:
case JS_MAP_TYPE:
@@ -8454,43 +8456,6 @@
#endif // ENABLE_DISASSEMBLER
-static void CopyFastElementsToFast(FixedArray* source,
- FixedArray* destination,
- WriteBarrierMode mode) {
- int count = source->length();
- int copy_size = Min(count, destination->length());
- if (mode == SKIP_WRITE_BARRIER ||
- !Page::FromAddress(destination->address())->IsFlagSet(
- MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING)) {
- Address to = destination->address() + FixedArray::kHeaderSize;
- Address from = source->address() + FixedArray::kHeaderSize;
- memcpy(reinterpret_cast<void*>(to),
- reinterpret_cast<void*>(from),
- kPointerSize * copy_size);
- } else {
- for (int i = 0; i < copy_size; ++i) {
- destination->set(i, source->get(i), mode);
- }
- }
-}
-
-
-static void CopySlowElementsToFast(SeededNumberDictionary* source,
- FixedArray* destination,
- WriteBarrierMode mode) {
- int destination_length = destination->length();
- for (int i = 0; i < source->Capacity(); ++i) {
- Object* key = source->KeyAt(i);
- if (key->IsNumber()) {
- uint32_t entry = static_cast<uint32_t>(key->Number());
- if (entry < static_cast<uint32_t>(destination_length)) {
- destination->set(entry, source->ValueAt(i), mode);
- }
- }
- }
-}
-
-
MaybeObject* JSObject::SetFastElementsCapacityAndLength(
int capacity,
int length,
@@ -8524,79 +8489,17 @@
FixedArrayBase* old_elements_raw = elements();
ElementsKind elements_kind = GetElementsKind();
- switch (elements_kind) {
- case FAST_SMI_ONLY_ELEMENTS:
- case FAST_ELEMENTS: {
- AssertNoAllocation no_gc;
- WriteBarrierMode mode(new_elements->GetWriteBarrierMode(no_gc));
- CopyFastElementsToFast(FixedArray::cast(old_elements_raw),
- new_elements, mode);
- set_map_and_elements(new_map, new_elements);
- break;
- }
- case DICTIONARY_ELEMENTS: {
- AssertNoAllocation no_gc;
- WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc);
- CopySlowElementsToFast(SeededNumberDictionary::cast(old_elements_raw),
- new_elements,
- mode);
- set_map_and_elements(new_map, new_elements);
- break;
- }
- case NON_STRICT_ARGUMENTS_ELEMENTS: {
- AssertNoAllocation no_gc;
- WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc);
- // The object's map and the parameter map are unchanged, the unaliased
- // arguments are copied to the new backing store.
- FixedArray* parameter_map = FixedArray::cast(old_elements_raw);
- FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
- if (arguments->IsDictionary()) {
- CopySlowElementsToFast(SeededNumberDictionary::cast(arguments),
- new_elements,
- mode);
- } else {
- CopyFastElementsToFast(arguments, new_elements, mode);
- }
- parameter_map->set(1, new_elements);
- break;
- }
- case FAST_DOUBLE_ELEMENTS: {
- FixedDoubleArray* old_elements = FixedDoubleArray::cast(old_elements_raw);
- uint32_t old_length = static_cast<uint32_t>(old_elements->length());
- // Fill out the new array with this content and array holes.
- for (uint32_t i = 0; i < old_length; i++) {
- if (!old_elements->is_the_hole(i)) {
- Object* obj;
- // Objects must be allocated in the old object space, since the
- // overall number of HeapNumbers needed for the conversion might
- // exceed the capacity of new space, and we would fail repeatedly
- // trying to convert the FixedDoubleArray.
- MaybeObject* maybe_value_object =
- GetHeap()->AllocateHeapNumber(old_elements->get_scalar(i),
- TENURED);
- if (!maybe_value_object->To(&obj)) return maybe_value_object;
- // Force write barrier. It's not worth trying to exploit
- // elems->GetWriteBarrierMode(), since it requires an
- // AssertNoAllocation stack object that would have to be positioned
- // after the HeapNumber allocation anyway.
- new_elements->set(i, obj, UPDATE_WRITE_BARRIER);
- }
- }
- set_map(new_map);
- set_elements(new_elements);
- break;
- }
- case EXTERNAL_BYTE_ELEMENTS:
- case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
- case EXTERNAL_SHORT_ELEMENTS:
- case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
- case EXTERNAL_INT_ELEMENTS:
- case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS:
- case EXTERNAL_DOUBLE_ELEMENTS:
- case EXTERNAL_PIXEL_ELEMENTS:
- UNREACHABLE();
- break;
+ ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind);
+ ElementsKind to_kind = (elements_kind == FAST_SMI_ONLY_ELEMENTS)
+ ? FAST_SMI_ONLY_ELEMENTS
+ : FAST_ELEMENTS;
+ // int copy_size = Min(old_elements_raw->length(), new_elements->length());
+ accessor->CopyElements(this, new_elements, to_kind);
+ if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) {
+ set_map_and_elements(new_map, new_elements);
+ } else {
+ FixedArray* parameter_map = FixedArray::cast(old_elements_raw);
+ parameter_map->set(1, new_elements);
}
if (FLAG_trace_elements_transitions) {
@@ -12969,4 +12872,133 @@
#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 =
+ date_cache->ToLocal(static_cast<int64_t>(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 = date_cache->ToLocal(static_cast<int64_t>(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
diff --git a/src/objects.h b/src/objects.h
index 65cd6fb..7906d14 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -64,6 +64,7 @@
// - JSBuiltinsObject
// - JSGlobalProxy
// - JSValue
+// - JSDate
// - JSMessageObject
// - JSProxy
// - JSFunctionProxy
@@ -300,6 +301,7 @@
V(JS_MESSAGE_OBJECT_TYPE) \
\
V(JS_VALUE_TYPE) \
+ V(JS_DATE_TYPE) \
V(JS_OBJECT_TYPE) \
V(JS_CONTEXT_EXTENSION_OBJECT_TYPE) \
V(JS_GLOBAL_OBJECT_TYPE) \
@@ -619,6 +621,7 @@
JS_PROXY_TYPE, // LAST_JS_PROXY_TYPE
JS_VALUE_TYPE, // FIRST_JS_OBJECT_TYPE
+ JS_DATE_TYPE,
JS_OBJECT_TYPE,
JS_CONTEXT_EXTENSION_OBJECT_TYPE,
JS_GLOBAL_OBJECT_TYPE,
@@ -813,6 +816,7 @@
V(Oddball) \
V(SharedFunctionInfo) \
V(JSValue) \
+ V(JSDate) \
V(JSMessageObject) \
V(StringWrapper) \
V(Foreign) \
@@ -889,6 +893,7 @@
// Extract the number.
inline double Number();
+ inline bool IsNaN();
// Returns true if the object is of the correct type to be used as a
// implementation of a JSObject's elements.
@@ -5994,7 +5999,7 @@
};
-// Representation for JS Wrapper objects, String, Number, Boolean, Date, etc.
+// Representation for JS Wrapper objects, String, Number, Boolean, etc.
class JSValue: public JSObject {
public:
// [value]: the object being wrapped.
@@ -6023,6 +6028,106 @@
};
+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)
+ // [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)
+ // [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() {
+ JSDatePrint(stdout);
+ }
+ void JSDatePrint(FILE* out);
+#endif
+#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 kYearOffset = kValueOffset + kPointerSize;
+ static const int kMonthOffset = kYearOffset + kPointerSize;
+ static const int kDayOffset = kMonthOffset + 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 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);
+};
+
+
// Representation of message objects used for error reporting through
// the API. The messages are formatted in JavaScript so this object is
// a real JavaScript object. The information used for formatting the
diff --git a/src/profile-generator-inl.h b/src/profile-generator-inl.h
index 7a70b01..d967ed3 100644
--- a/src/profile-generator-inl.h
+++ b/src/profile-generator-inl.h
@@ -95,7 +95,7 @@
}
-uint64_t HeapObjectsMap::GetNthGcSubrootId(int delta) {
+SnapshotObjectId HeapObjectsMap::GetNthGcSubrootId(int delta) {
return kGcRootsFirstSubrootId + delta * kObjectIdStep;
}
@@ -115,10 +115,10 @@
}
-uint64_t HeapEntry::id() {
+SnapshotObjectId HeapEntry::id() {
union {
Id stored_id;
- uint64_t returned_id;
+ SnapshotObjectId returned_id;
} id_adaptor = {id_};
return id_adaptor.returned_id;
}
diff --git a/src/profile-generator.cc b/src/profile-generator.cc
index 14349cc..ca97565 100644
--- a/src/profile-generator.cc
+++ b/src/profile-generator.cc
@@ -965,7 +965,7 @@
void HeapEntry::Init(HeapSnapshot* snapshot,
Type type,
const char* name,
- uint64_t id,
+ SnapshotObjectId id,
int self_size,
int children_count,
int retainers_count) {
@@ -980,7 +980,7 @@
dominator_ = NULL;
union {
- uint64_t set_id;
+ SnapshotObjectId set_id;
Id stored_id;
} id_adaptor = {id};
id_ = id_adaptor.stored_id;
@@ -1096,9 +1096,9 @@
}
-int HeapEntry::EntriesSize(int entries_count,
- int children_count,
- int retainers_count) {
+size_t HeapEntry::EntriesSize(int entries_count,
+ int children_count,
+ int retainers_count) {
return sizeof(HeapEntry) * entries_count // NOLINT
+ sizeof(HeapGraphEdge) * children_count // NOLINT
+ sizeof(HeapGraphEdge*) * retainers_count; // NOLINT
@@ -1114,13 +1114,14 @@
template <> struct SnapshotSizeConstants<4> {
static const int kExpectedHeapGraphEdgeSize = 12;
static const int kExpectedHeapEntrySize = 36;
- static const int kMaxSerializableSnapshotRawSize = 256 * MB;
+ static const size_t kMaxSerializableSnapshotRawSize = 256 * MB;
};
template <> struct SnapshotSizeConstants<8> {
static const int kExpectedHeapGraphEdgeSize = 24;
static const int kExpectedHeapEntrySize = 48;
- static const int kMaxSerializableSnapshotRawSize = 768 * MB;
+ static const uint64_t kMaxSerializableSnapshotRawSize =
+ static_cast<uint64_t>(6000) * MB;
};
} // namespace
@@ -1221,7 +1222,7 @@
HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
const char* name,
- uint64_t id,
+ SnapshotObjectId id,
int size,
int children_count,
int retainers_count) {
@@ -1253,7 +1254,7 @@
}
-HeapEntry* HeapSnapshot::GetEntryById(uint64_t id) {
+HeapEntry* HeapSnapshot::GetEntryById(SnapshotObjectId id) {
List<HeapEntry*>* entries_by_id = GetSortedEntriesList();
// Perform a binary search by id.
@@ -1262,7 +1263,7 @@
while (low <= high) {
int mid =
(static_cast<unsigned int>(low) + static_cast<unsigned int>(high)) >> 1;
- uint64_t mid_id = entries_by_id->at(mid)->id();
+ SnapshotObjectId mid_id = entries_by_id->at(mid)->id();
if (mid_id > id)
high = mid - 1;
else if (mid_id < id)
@@ -1298,12 +1299,12 @@
// We split IDs on evens for embedder objects (see
// HeapObjectsMap::GenerateId) and odds for native objects.
-const uint64_t HeapObjectsMap::kInternalRootObjectId = 1;
-const uint64_t HeapObjectsMap::kGcRootsObjectId =
+const SnapshotObjectId HeapObjectsMap::kInternalRootObjectId = 1;
+const SnapshotObjectId HeapObjectsMap::kGcRootsObjectId =
HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep;
-const uint64_t HeapObjectsMap::kGcRootsFirstSubrootId =
+const SnapshotObjectId HeapObjectsMap::kGcRootsFirstSubrootId =
HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep;
-const uint64_t HeapObjectsMap::kFirstAvailableObjectId =
+const SnapshotObjectId HeapObjectsMap::kFirstAvailableObjectId =
HeapObjectsMap::kGcRootsFirstSubrootId +
VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep;
@@ -1325,12 +1326,12 @@
}
-uint64_t HeapObjectsMap::FindObject(Address addr) {
+SnapshotObjectId HeapObjectsMap::FindObject(Address addr) {
if (!initial_fill_mode_) {
- uint64_t existing = FindEntry(addr);
+ SnapshotObjectId existing = FindEntry(addr);
if (existing != 0) return existing;
}
- uint64_t id = next_id_;
+ SnapshotObjectId id = next_id_;
next_id_ += kObjectIdStep;
AddEntry(addr, id);
return id;
@@ -1353,7 +1354,7 @@
}
-void HeapObjectsMap::AddEntry(Address addr, uint64_t id) {
+void HeapObjectsMap::AddEntry(Address addr, SnapshotObjectId id) {
HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true);
ASSERT(entry->value == NULL);
entry->value = reinterpret_cast<void*>(entries_->length());
@@ -1361,7 +1362,7 @@
}
-uint64_t HeapObjectsMap::FindEntry(Address addr) {
+SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) {
HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), false);
if (entry != NULL) {
int entry_index =
@@ -1401,8 +1402,8 @@
}
-uint64_t HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
- uint64_t id = static_cast<uint64_t>(info->GetHash());
+SnapshotObjectId HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
+ SnapshotObjectId id = static_cast<SnapshotObjectId>(info->GetHash());
const char* label = info->GetLabel();
id ^= HashSequentialString(label,
static_cast<int>(strlen(label)),
@@ -1472,7 +1473,8 @@
}
-Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(uint64_t id) {
+Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(
+ SnapshotObjectId id) {
// First perform a full GC in order to avoid dead objects.
HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask,
"HeapSnapshotsCollection::FindHeapObjectById");
@@ -3436,8 +3438,8 @@
HeapEntry* root = result->AddRootEntry(1);
const char* text = snapshot_->collection()->names()->GetFormatted(
"The snapshot is too big. "
- "Maximum snapshot size is %d MB. "
- "Actual snapshot size is %d MB.",
+ "Maximum snapshot size is %" V8_PTR_PREFIX "u MB. "
+ "Actual snapshot size is %" V8_PTR_PREFIX "u MB.",
SnapshotSizeConstants<kPointerSize>::kMaxSerializableSnapshotRawSize / MB,
(snapshot_->raw_entries_size() + MB - 1) / MB);
HeapEntry* message = result->AddEntry(
@@ -3526,21 +3528,21 @@
void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
- // The buffer needs space for 6 ints, 1 uint64_t, 7 commas, \n and \0
+ // The buffer needs space for 6 ints, 1 uint32_t, 7 commas, \n and \0
static const int kBufferSize =
6 * MaxDecimalDigitsIn<sizeof(int)>::kSigned // NOLINT
- + MaxDecimalDigitsIn<sizeof(uint64_t)>::kUnsigned // NOLINT
+ + MaxDecimalDigitsIn<sizeof(uint32_t)>::kUnsigned // NOLINT
+ 7 + 1 + 1;
EmbeddedVector<char, kBufferSize> buffer;
Vector<HeapGraphEdge> children = entry->children();
STATIC_CHECK(sizeof(int) == sizeof(entry->type())); // NOLINT
STATIC_CHECK(sizeof(int) == sizeof(GetStringId(entry->name()))); // NOLINT
- STATIC_CHECK(sizeof(uint64_t) == sizeof(entry->id())); // NOLINT
+ STATIC_CHECK(sizeof(unsigned) == sizeof(entry->id())); // NOLINT
STATIC_CHECK(sizeof(int) == sizeof(entry->self_size())); // NOLINT
STATIC_CHECK(sizeof(int) == sizeof(entry->retained_size())); // NOLINT
STATIC_CHECK(sizeof(int) == sizeof(GetNodeId(entry->dominator()))); // NOLINT
STATIC_CHECK(sizeof(int) == sizeof(children.length())); // NOLINT
- int result = OS::SNPrintF(buffer, "\n,%d,%d,%llu,%d,%d,%d,%d",
+ int result = OS::SNPrintF(buffer, "\n,%d,%d,%u,%d,%d,%d,%d",
entry->type(),
GetStringId(entry->name()),
entry->id(),
diff --git a/src/profile-generator.h b/src/profile-generator.h
index f9ae5f9..fadae7e 100644
--- a/src/profile-generator.h
+++ b/src/profile-generator.h
@@ -35,6 +35,8 @@
namespace v8 {
namespace internal {
+typedef uint32_t SnapshotObjectId;
+
class TokenEnumerator {
public:
TokenEnumerator();
@@ -533,7 +535,7 @@
void Init(HeapSnapshot* snapshot,
Type type,
const char* name,
- uint64_t id,
+ SnapshotObjectId id,
int self_size,
int children_count,
int retainers_count);
@@ -542,7 +544,7 @@
Type type() { return static_cast<Type>(type_); }
const char* name() { return name_; }
void set_name(const char* name) { name_ = name; }
- inline uint64_t id();
+ inline SnapshotObjectId id();
int self_size() { return self_size_; }
int retained_size() { return retained_size_; }
void add_retained_size(int size) { retained_size_ += size; }
@@ -575,16 +577,18 @@
int retainer_index);
void SetUnidirElementReference(int child_index, int index, HeapEntry* entry);
- int EntrySize() { return EntriesSize(1, children_count_, retainers_count_); }
+ size_t EntrySize() {
+ return EntriesSize(1, children_count_, retainers_count_);
+ }
void Print(
const char* prefix, const char* edge_name, int max_depth, int indent);
Handle<HeapObject> GetHeapObject();
- static int EntriesSize(int entries_count,
- int children_count,
- int retainers_count);
+ static size_t EntriesSize(int entries_count,
+ int children_count,
+ int retainers_count);
private:
HeapGraphEdge* children_arr() {
@@ -645,13 +649,13 @@
HeapEntry* natives_root() { return natives_root_entry_; }
HeapEntry* gc_subroot(int index) { return gc_subroot_entries_[index]; }
List<HeapEntry*>* entries() { return &entries_; }
- int raw_entries_size() { return raw_entries_size_; }
+ size_t raw_entries_size() { return raw_entries_size_; }
void AllocateEntries(
int entries_count, int children_count, int retainers_count);
HeapEntry* AddEntry(HeapEntry::Type type,
const char* name,
- uint64_t id,
+ SnapshotObjectId id,
int size,
int children_count,
int retainers_count);
@@ -662,7 +666,7 @@
int retainers_count);
HeapEntry* AddNativesRootEntry(int children_count, int retainers_count);
void ClearPaint();
- HeapEntry* GetEntryById(uint64_t id);
+ HeapEntry* GetEntryById(SnapshotObjectId id);
List<HeapEntry*>* GetSortedEntriesList();
template<class Visitor>
void IterateEntries(Visitor* visitor) { entries_.Iterate(visitor); }
@@ -685,7 +689,7 @@
char* raw_entries_;
List<HeapEntry*> entries_;
bool entries_sorted_;
- int raw_entries_size_;
+ size_t raw_entries_size_;
friend class HeapSnapshotTester;
@@ -699,29 +703,31 @@
~HeapObjectsMap();
void SnapshotGenerationFinished();
- uint64_t FindObject(Address addr);
+ SnapshotObjectId FindObject(Address addr);
void MoveObject(Address from, Address to);
- static uint64_t GenerateId(v8::RetainedObjectInfo* info);
- static inline uint64_t GetNthGcSubrootId(int delta);
+ static SnapshotObjectId GenerateId(v8::RetainedObjectInfo* info);
+ static inline SnapshotObjectId GetNthGcSubrootId(int delta);
static const int kObjectIdStep = 2;
- static const uint64_t kInternalRootObjectId;
- static const uint64_t kGcRootsObjectId;
- static const uint64_t kNativesRootObjectId;
- static const uint64_t kGcRootsFirstSubrootId;
- static const uint64_t kFirstAvailableObjectId;
+ static const SnapshotObjectId kInternalRootObjectId;
+ static const SnapshotObjectId kGcRootsObjectId;
+ static const SnapshotObjectId kNativesRootObjectId;
+ static const SnapshotObjectId kGcRootsFirstSubrootId;
+ static const SnapshotObjectId kFirstAvailableObjectId;
private:
struct EntryInfo {
- explicit EntryInfo(uint64_t id) : id(id), accessed(true) { }
- EntryInfo(uint64_t id, bool accessed) : id(id), accessed(accessed) { }
- uint64_t id;
+ explicit EntryInfo(SnapshotObjectId id) : id(id), accessed(true) { }
+ EntryInfo(SnapshotObjectId id, bool accessed)
+ : id(id),
+ accessed(accessed) { }
+ SnapshotObjectId id;
bool accessed;
};
- void AddEntry(Address addr, uint64_t id);
- uint64_t FindEntry(Address addr);
+ void AddEntry(Address addr, SnapshotObjectId id);
+ SnapshotObjectId FindEntry(Address addr);
void RemoveDeadEntries();
static bool AddressesMatch(void* key1, void* key2) {
@@ -735,7 +741,7 @@
}
bool initial_fill_mode_;
- uint64_t next_id_;
+ SnapshotObjectId next_id_;
HashMap entries_map_;
List<EntryInfo>* entries_;
@@ -760,8 +766,8 @@
StringsStorage* names() { return &names_; }
TokenEnumerator* token_enumerator() { return token_enumerator_; }
- uint64_t GetObjectId(Address addr) { return ids_.FindObject(addr); }
- Handle<HeapObject> FindHeapObjectById(uint64_t id);
+ SnapshotObjectId GetObjectId(Address addr) { return ids_.FindObject(addr); }
+ Handle<HeapObject> FindHeapObjectById(SnapshotObjectId id);
void ObjectMoveEvent(Address from, Address to) { ids_.MoveObject(from, to); }
private:
diff --git a/src/runtime.cc b/src/runtime.cc
index 019851e..cc5aeab 100644
--- a/src/runtime.cc
+++ b/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 @@
}
-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,44 @@
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 if (!is_utc &&
+ (time < -DateCache::kMaxTimeBeforeUTCInMs ||
+ time > DateCache::kMaxTimeBeforeUTCInMs)) {
+ value = isolate->heap()->nan_value();
+ is_value_nan = true;
} else {
- day = kDayInYear[date];
- month = kMonthInYear[date];
+ time = is_utc ? time : date_cache->ToUTC(static_cast<int64_t>(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 +9063,20 @@
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(static_cast<double>(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(static_cast<double>(time));
}
diff --git a/src/runtime.h b/src/runtime.h
index 1f9566b..c5ce3c3 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -244,10 +244,9 @@
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,6 +490,7 @@
F(Arguments, 1, 1) \
F(ValueOf, 1, 1) \
F(SetValueOf, 2, 1) \
+ F(DateField, 2 /* date object, field index */, 1) \
F(StringCharFromCode, 1, 1) \
F(StringCharAt, 2, 1) \
F(ObjectEquals, 2, 1) \
diff --git a/src/scopes.cc b/src/scopes.cc
index 766e1ae..859cbd1 100644
--- a/src/scopes.cc
+++ b/src/scopes.cc
@@ -1006,7 +1006,7 @@
Isolate* isolate = Isolate::Current();
Factory* factory = isolate->factory();
Handle<JSArray> array = factory->NewJSArray(1);
- array->SetElement(array, 0, var->name(), NONE, kStrictMode);
+ USE(JSObject::SetElement(array, 0, var->name(), NONE, kStrictMode));
Handle<Object> result =
factory->NewSyntaxError("module_type_error", array);
isolate->Throw(*result, &location);
diff --git a/src/serialize.cc b/src/serialize.cc
index 8d8dbfd..4249d36 100644
--- a/src/serialize.cc
+++ b/src/serialize.cc
@@ -502,6 +502,14 @@
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");
}
diff --git a/src/version.cc b/src/version.cc
index 57c60d0..57e6594 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 3
#define MINOR_VERSION 9
-#define BUILD_NUMBER 16
+#define BUILD_NUMBER 17
#define PATCH_LEVEL 0
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
index 1354e62..d616749 100644
--- a/src/x64/code-stubs-x64.cc
+++ b/src/x64/code-stubs-x64.cc
@@ -5454,7 +5454,7 @@
__ movb(scratch, Operand(left, index, times_1, 0));
__ cmpb(scratch, Operand(right, index, times_1, 0));
__ j(not_equal, chars_not_equal, near_jump);
- __ addq(index, Immediate(1));
+ __ incq(index);
__ j(not_zero, &loop);
}
@@ -5625,9 +5625,10 @@
void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::STRINGS);
- ASSERT(GetCondition() == equal);
Label miss;
+ bool equality = Token::IsEqualityOp(op_);
+
// Registers containing left and right operands respectively.
Register left = rdx;
Register right = rax;
@@ -5665,24 +5666,31 @@
// Check that both strings are symbols. If they are, we're done
// because we already know they are not identical.
- Label do_compare;
- STATIC_ASSERT(kSymbolTag != 0);
- __ and_(tmp1, tmp2);
- __ testb(tmp1, Immediate(kIsSymbolMask));
- __ j(zero, &do_compare, Label::kNear);
- // Make sure rax is non-zero. At this point input operands are
- // guaranteed to be non-zero.
- ASSERT(right.is(rax));
- __ ret(0);
+ if (equality) {
+ Label do_compare;
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ and_(tmp1, tmp2);
+ __ testb(tmp1, Immediate(kIsSymbolMask));
+ __ j(zero, &do_compare, Label::kNear);
+ // Make sure rax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(rax));
+ __ ret(0);
+ __ bind(&do_compare);
+ }
// Check that both strings are sequential ASCII.
Label runtime;
- __ bind(&do_compare);
__ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
// Compare flat ASCII strings. Returns when done.
- StringCompareStub::GenerateFlatAsciiStringEquals(
- masm, left, right, tmp1, tmp2);
+ if (equality) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(
+ masm, left, right, tmp1, tmp2);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(
+ masm, left, right, tmp1, tmp2, tmp3, kScratchRegister);
+ }
// Handle more complex cases in runtime.
__ bind(&runtime);
@@ -5690,7 +5698,11 @@
__ push(left);
__ push(right);
__ push(tmp1);
- __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+ if (equality) {
+ __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+ } else {
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+ }
__ bind(&miss);
GenerateMiss(masm);
diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index 49adf6a..6739cc8 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -2834,6 +2834,54 @@
}
+void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+ ASSERT_NE(NULL, args->at(1)->AsLiteral());
+ 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(object);
+ __ CmpObjectType(object, JS_DATE_TYPE, scratch);
+ __ Assert(equal, "Trying to get date field from non-date.");
+#endif
+
+ 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);
+}
+
+
void FullCodeGenerator::EmitMathPow(CallRuntime* expr) {
// Load the arguments on the stack and call the runtime function.
ZoneList<Expression*>* args = expr->arguments();
diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc
index 893ec59..f707df0 100644
--- a/src/x64/lithium-codegen-x64.cc
+++ b/src/x64/lithium-codegen-x64.cc
@@ -1224,6 +1224,49 @@
}
+void LCodeGen::DoDateField(LDateField* instr) {
+ Register object = ToRegister(instr->InputAt(0));
+ Register result = ToRegister(instr->result());
+ Smi* index = instr->index();
+ Label runtime, done;
+ ASSERT(object.is(result));
+ ASSERT(object.is(rax));
+
+#ifdef DEBUG
+ __ AbortIfSmi(object);
+ __ CmpObjectType(object, JS_DATE_TYPE, kScratchRegister);
+ __ Assert(equal, "Trying to get date field from non-date.");
+#endif
+
+ 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
+ __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ bind(&done);
+ }
+}
+
+
void LCodeGen::DoBitNotI(LBitNotI* instr) {
LOperand* input = instr->InputAt(0);
ASSERT(input->Equals(instr->result()));
diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc
index 79ca5f5..e2569c9 100644
--- a/src/x64/lithium-x64.cc
+++ b/src/x64/lithium-x64.cc
@@ -1601,6 +1601,13 @@
}
+LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
+ LOperand* object = UseFixed(instr->value(), rax);
+ LDateField* result = new LDateField(object, instr->index());
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) {
LOperand* value = UseRegisterOrConstantAtStart(instr->index());
LOperand* length = Use(instr->length());
diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h
index 5f5a201..3d5d854 100644
--- a/src/x64/lithium-x64.h
+++ b/src/x64/lithium-x64.h
@@ -177,7 +177,8 @@
V(ForInPrepareMap) \
V(ForInCacheArray) \
V(CheckMapValue) \
- V(LoadFieldByIndex)
+ V(LoadFieldByIndex) \
+ V(DateField)
#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
@@ -987,6 +988,22 @@
};
+class LDateField: public LTemplateInstruction<1, 1, 0> {
+ public:
+ LDateField(LOperand* date, Smi* index) : index_(index) {
+ inputs_[0] = date;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(ValueOf, "date-field")
+ DECLARE_HYDROGEN_ACCESSOR(ValueOf)
+
+ Smi* index() const { return index_; }
+
+ private:
+ Smi* index_;
+};
+
+
class LThrow: public LTemplateInstruction<0, 1, 0> {
public:
explicit LThrow(LOperand* value) {
diff --git a/test/cctest/SConscript b/test/cctest/SConscript
index e512a33..bcd1e98 100644
--- a/test/cctest/SConscript
+++ b/test/cctest/SConscript
@@ -62,6 +62,7 @@
'test-conversions.cc',
'test-cpu-profiler.cc',
'test-dataflow.cc',
+ 'test-date.cc',
'test-debug.cc',
'test-decls.cc',
'test-deoptimization.cc',
diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp
index d8a8fc4..a242fe3 100644
--- a/test/cctest/cctest.gyp
+++ b/test/cctest/cctest.gyp
@@ -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',
diff --git a/test/cctest/test-date.cc b/test/cctest/test-date.cc
new file mode 100644
index 0000000..903a63a
--- /dev/null
+++ b/test/cctest/test-date.cc
@@ -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);
+}
diff --git a/test/mjsunit/date.js b/test/mjsunit/date.js
index fa43cbb..3e153ab 100644
--- a/test/mjsunit/date.js
+++ b/test/mjsunit/date.js
@@ -187,6 +187,12 @@
assertTrue(isNaN(d.getTime()));
d = new Date(1969, 12, 1, -Infinity);
assertTrue(isNaN(d.getTime()));
+d = new Date(1969, 12, 1, 0);
+d.setTime(Math.pow(2, 64));
+assertTrue(isNaN(d.getTime()));
+d = new Date(1969, 12, 1, 0);
+d.setTime(Math.pow(-2, 64));
+assertTrue(isNaN(d.getTime()));
// Test creation with obscure date values.
diff --git a/test/mjsunit/fuzz-natives.js b/test/mjsunit/fuzz-natives.js
index c4d18d0..2965e74 100644
--- a/test/mjsunit/fuzz-natives.js
+++ b/test/mjsunit/fuzz-natives.js
@@ -184,8 +184,9 @@
"RegExpConstructResult": true,
"_RegExpConstructResult": true,
- // This function performs some checks compile time (it requires its first
- // argument to be a compile time smi).
+ // This functions perform some checks compile time (they require one of their
+ // arguments to be a compile time smi).
+ "_DateField": true,
"_GetFromCache": true,
// This function expects its first argument to be a non-smi.
diff --git a/tools/gen-postmortem-metadata.py b/tools/gen-postmortem-metadata.py
index 4aa8f5d..b9b1625 100644
--- a/tools/gen-postmortem-metadata.py
+++ b/tools/gen-postmortem-metadata.py
@@ -115,6 +115,9 @@
'ConsString, second, String, kSecondOffset',
'ExternalString, resource, Object, kResourceOffset',
'SeqAsciiString, chars, char, kHeaderSize',
+ 'SharedFunctionInfo, code, Code, kCodeOffset',
+ 'Code, instruction_start, uintptr_t, kHeaderSize',
+ 'Code, instruction_size, int, kInstructionSizeOffset',
];
#
diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp
index 32a5f56..b876951 100644
--- a/tools/gyp/v8.gyp
+++ b/tools/gyp/v8.gyp
@@ -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',