static const int kFullStringRepresentationMask = 0x07;
static const int kExternalTwoByteRepresentationTag = 0x02;
- static const int kJSObjectType = 0xa2;
+ static const int kJSObjectType = 0xa3;
static const int kFirstNonstringType = 0x80;
static const int kForeignType = 0x85;
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
}
+Handle<FixedArray> Factory::NewFixedDoubleArray(int size,
+ PretenureFlag pretenure) {
+ ASSERT(0 <= size);
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateUninitializedFixedDoubleArray(size, pretenure),
+ FixedArray);
+}
+
+
Handle<StringDictionary> Factory::NewStringDictionary(int at_least_space_for) {
ASSERT(0 <= at_least_space_for);
CALL_HEAP_FUNCTION(isolate(),
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
class Factory {
public:
- // Allocate a new fixed array with undefined entries.
+ // Allocate a new uninitialized fixed array.
Handle<FixedArray> NewFixedArray(
int size,
PretenureFlag pretenure = NOT_TENURED);
int size,
PretenureFlag pretenure = NOT_TENURED);
+ // Allocate a new uninitialized fixed double array.
+ Handle<FixedArray> NewFixedDoubleArray(
+ int size,
+ PretenureFlag pretenure = NOT_TENURED);
+
Handle<NumberDictionary> NewNumberDictionary(int at_least_space_for);
Handle<StringDictionary> NewStringDictionary(int at_least_space_for);
// Flags for experimental language features.
DEFINE_bool(harmony_proxies, false, "enable harmony proxies")
+// Flags for experimental implementation features.
+DEFINE_bool(unbox_double_arrays, false, "automatically unbox arrays of doubles")
+
// Flags for Crankshaft.
#ifdef V8_TARGET_ARCH_MIPS
DEFINE_bool(crankshaft, false, "use crankshaft")
table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate);
table_.Register(kVisitByteArray, &EvacuateByteArray);
table_.Register(kVisitFixedArray, &EvacuateFixedArray);
+ table_.Register(kVisitFixedDoubleArray, &EvacuateFixedDoubleArray);
table_.Register(kVisitGlobalContext,
&ObjectEvacuationStrategy<POINTER_OBJECT>::
}
+ static inline void EvacuateFixedDoubleArray(Map* map,
+ HeapObject** slot,
+ HeapObject* object) {
+ int length = reinterpret_cast<FixedDoubleArray*>(object)->length();
+ int object_size = FixedDoubleArray::SizeFor(length);
+ EvacuateObject<DATA_OBJECT, UNKNOWN_SIZE>(map,
+ slot,
+ object,
+ object_size);
+ }
+
+
static inline void EvacuateByteArray(Map* map,
HeapObject** slot,
HeapObject* object) {
Map::cast(obj)->set_is_undetectable();
{ MaybeObject* maybe_obj =
+ AllocateMap(FIXED_DOUBLE_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_fixed_double_array_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj =
AllocateMap(BYTE_ARRAY_TYPE, kVariableSizeSentinel);
if (!maybe_obj->ToObject(&obj)) return false;
}
}
+MaybeObject* Heap::AllocateEmptyFixedDoubleArray() {
+ int size = FixedDoubleArray::SizeFor(0);
+ Object* result;
+ { MaybeObject* maybe_result =
+ AllocateRaw(size, OLD_DATA_SPACE, OLD_DATA_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ // Initialize the object.
+ reinterpret_cast<FixedDoubleArray*>(result)->set_map(
+ fixed_double_array_map());
+ reinterpret_cast<FixedDoubleArray*>(result)->set_length(0);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateUninitializedFixedDoubleArray(
+ int length,
+ PretenureFlag pretenure) {
+ if (length == 0) return empty_fixed_double_array();
+
+ Object* obj;
+ { MaybeObject* maybe_obj = AllocateRawFixedDoubleArray(length, pretenure);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ reinterpret_cast<FixedDoubleArray*>(obj)->set_map(fixed_double_array_map());
+ FixedDoubleArray::cast(obj)->set_length(length);
+ return obj;
+}
+
+
+MaybeObject* Heap::AllocateRawFixedDoubleArray(int length,
+ PretenureFlag pretenure) {
+ if (length < 0 || length > FixedDoubleArray::kMaxLength) {
+ return Failure::OutOfMemoryException();
+ }
+
+ AllocationSpace space =
+ (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
+ int size = FixedDoubleArray::SizeFor(length);
+ if (space == NEW_SPACE && size > kMaxObjectSizeInNewSpace) {
+ // Too big for new space.
+ space = LO_SPACE;
+ } else if (space == OLD_DATA_SPACE &&
+ size > MaxObjectSizeInPagedSpace()) {
+ // Too big for old data space.
+ space = LO_SPACE;
+ }
+
+ AllocationSpace retry_space =
+ (size <= MaxObjectSizeInPagedSpace()) ? OLD_DATA_SPACE : LO_SPACE;
+
+ return AllocateRaw(size, space, retry_space);
+}
+
+
MaybeObject* Heap::AllocateHashTable(int length, PretenureFlag pretenure) {
Object* result;
{ MaybeObject* maybe_result = AllocateFixedArray(length, pretenure);
V(Map, global_context_map, GlobalContextMap) \
V(Map, fixed_array_map, FixedArrayMap) \
V(Map, fixed_cow_array_map, FixedCOWArrayMap) \
+ V(Map, fixed_double_array_map, FixedDoubleArrayMap) \
V(Object, no_interceptor_result_sentinel, NoInterceptorResultSentinel) \
V(Map, meta_map, MetaMap) \
V(Map, hash_table_map, HashTableMap) \
V(Object, termination_exception, TerminationException) \
V(FixedArray, empty_fixed_array, EmptyFixedArray) \
V(ByteArray, empty_byte_array, EmptyByteArray) \
+ V(FixedDoubleArray, empty_fixed_double_array, EmptyFixedDoubleArray) \
V(String, empty_string, EmptyString) \
V(DescriptorArray, empty_descriptor_array, EmptyDescriptorArray) \
V(Map, string_map, StringMap) \
int length,
PretenureFlag pretenure = NOT_TENURED);
+ MUST_USE_RESULT MaybeObject* AllocateRawFixedDoubleArray(
+ int length,
+ PretenureFlag pretenure);
+
+ // Allocates a fixed double array with uninitialized values. Returns
+ // Failure::RetryAfterGC(requested_bytes, space) if the allocation failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateUninitializedFixedDoubleArray(
+ int length,
+ PretenureFlag pretenure = NOT_TENURED);
+
// AllocateHashTable is identical to AllocateFixedArray except
// that the resulting object has hash_table_map as map.
MUST_USE_RESULT MaybeObject* AllocateHashTable(
// Allocate empty fixed array.
MUST_USE_RESULT MaybeObject* AllocateEmptyFixedArray();
+ // Allocate empty fixed double array.
+ MUST_USE_RESULT MaybeObject* AllocateEmptyFixedDoubleArray();
+
void SwitchScavengingVisitorsTableIfProfilingWasEnabled();
// Performs a minor collection in new generation.
FixedArray::BodyDescriptor,
void>::Visit);
+ table_.Register(kVisitFixedDoubleArray, DataObjectVisitor::Visit);
+
table_.Register(kVisitGlobalContext,
&FixedBodyVisitor<StaticMarkingVisitor,
Context::MarkCompactBodyDescriptor,
case FIXED_ARRAY_TYPE:
FixedArray::cast(this)->FixedArrayVerify();
break;
+ case FIXED_DOUBLE_ARRAY_TYPE:
+ FixedDoubleArray::cast(this)->FixedDoubleArrayVerify();
+ break;
case BYTE_ARRAY_TYPE:
ByteArray::cast(this)->ByteArrayVerify();
break;
}
+void FixedDoubleArray::FixedDoubleArrayVerify() {
+ for (int i = 0; i < length(); i++) {
+ if (!is_the_hole(i)) {
+ double value = get(i);
+ ASSERT(!isnan(value) ||
+ BitCast<uint64_t>(value) == kCanonicalNonHoleNanInt64);
+ }
+ }
+}
+
+
void JSValue::JSValueVerify() {
Object* v = value();
if (v->IsHeapObject()) {
void JSArray::JSArrayVerify() {
JSObjectVerify();
ASSERT(length()->IsNumber() || length()->IsUndefined());
- ASSERT(elements()->IsUndefined() || elements()->IsFixedArray());
+ ASSERT(elements()->IsUndefined() ||
+ elements()->IsFixedArray() ||
+ elements()->IsFixedDoubleArray());
}
String::cast(this)->IsTwoByteRepresentation();
}
+bool Object::HasValidElements() {
+ // Dictionary is covered under FixedArray.
+ return IsFixedArray() || IsFixedDoubleArray() || IsExternalArray();
+}
StringShape::StringShape(String* str)
: type_(str->map()->instance_type()) {
}
+bool Object::IsFixedDoubleArray() {
+ return Object::IsHeapObject()
+ && HeapObject::cast(this)->map()->instance_type() ==
+ FIXED_DOUBLE_ARRAY_TYPE;
+}
+
+
bool Object::IsDescriptorArray() {
return IsFixedArray();
}
HeapObject* JSObject::elements() {
Object* array = READ_FIELD(this, kElementsOffset);
- // In the assert below Dictionary is covered under FixedArray.
- ASSERT(array->IsFixedArray() || array->IsExternalArray());
+ ASSERT(array->HasValidElements());
return reinterpret_cast<HeapObject*>(array);
}
ASSERT(map()->has_fast_elements() ==
(value->map() == GetHeap()->fixed_array_map() ||
value->map() == GetHeap()->fixed_cow_array_map()));
- // In the assert below Dictionary is covered under FixedArray.
- ASSERT(value->IsFixedArray() || value->IsExternalArray());
+ ASSERT(value->HasValidElements());
WRITE_FIELD(this, kElementsOffset, value);
CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kElementsOffset, mode);
}
}
+FixedArrayBase* FixedArrayBase::cast(Object* object) {
+ ASSERT(object->IsFixedArray() || object->IsFixedDoubleArray());
+ return reinterpret_cast<FixedArrayBase*>(object);
+}
+
+
Object* FixedArray::get(int index) {
ASSERT(index >= 0 && index < this->length());
return READ_FIELD(this, kHeaderSize + index * kPointerSize);
}
+double FixedDoubleArray::get(int index) {
+ ASSERT(map() != HEAP->fixed_cow_array_map() &&
+ map() != HEAP->fixed_array_map());
+ ASSERT(index >= 0 && index < this->length());
+ double result = READ_DOUBLE_FIELD(this, kHeaderSize + index * kDoubleSize);
+ ASSERT(!is_the_hole_nan(result));
+ return result;
+}
+
+
+void FixedDoubleArray::set(int index, double value) {
+ ASSERT(map() != HEAP->fixed_cow_array_map() &&
+ map() != HEAP->fixed_array_map());
+ int offset = kHeaderSize + index * kDoubleSize;
+ if (isnan(value)) value = canonical_not_the_hole_nan_as_double();
+ WRITE_DOUBLE_FIELD(this, offset, value);
+}
+
+
+void FixedDoubleArray::set_the_hole(int index) {
+ ASSERT(map() != HEAP->fixed_cow_array_map() &&
+ map() != HEAP->fixed_array_map());
+ int offset = kHeaderSize + index * kDoubleSize;
+ WRITE_DOUBLE_FIELD(this, offset, hole_nan_as_double());
+}
+
+
+bool FixedDoubleArray::is_the_hole(int index) {
+ int offset = kHeaderSize + index * kDoubleSize;
+ return is_the_hole_nan(READ_DOUBLE_FIELD(this, offset));
+}
+
+
+void FixedDoubleArray::Initialize(FixedDoubleArray* from) {
+ int old_length = from->length();
+ ASSERT(old_length < length());
+ OS::MemCopy(FIELD_ADDR(this, kHeaderSize),
+ FIELD_ADDR(from, kHeaderSize),
+ old_length * kDoubleSize);
+ int offset = kHeaderSize + old_length * kDoubleSize;
+ for (int current = from->length(); current < length(); ++current) {
+ WRITE_DOUBLE_FIELD(this, offset, hole_nan_as_double());
+ offset += kDoubleSize;
+ }
+}
+
+
+void FixedDoubleArray::Initialize(FixedArray* from) {
+ int old_length = from->length();
+ ASSERT(old_length < length());
+ for (int i = 0; i < old_length; i++) {
+ Object* hole_or_object = from->get(i);
+ if (hole_or_object->IsTheHole()) {
+ set_the_hole(i);
+ } else {
+ set(i, hole_or_object->Number());
+ }
+ }
+ int offset = kHeaderSize + old_length * kDoubleSize;
+ for (int current = from->length(); current < length(); ++current) {
+ WRITE_DOUBLE_FIELD(this, offset, hole_nan_as_double());
+ offset += kDoubleSize;
+ }
+}
+
+
+void FixedDoubleArray::Initialize(NumberDictionary* from) {
+ int offset = kHeaderSize;
+ for (int current = 0; current < length(); ++current) {
+ WRITE_DOUBLE_FIELD(this, offset, hole_nan_as_double());
+ offset += kDoubleSize;
+ }
+ 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());
+ set(entry, from->ValueAt(i)->Number());
+ }
+ }
+}
+
+
WriteBarrierMode HeapObject::GetWriteBarrierMode(const AssertNoAllocation&) {
if (GetHeap()->InNewSpace(this)) return SKIP_WRITE_BARRIER;
return UPDATE_WRITE_BARRIER;
CAST_ACCESSOR(FixedArray)
+CAST_ACCESSOR(FixedDoubleArray)
CAST_ACCESSOR(DescriptorArray)
CAST_ACCESSOR(DeoptimizationInputData)
CAST_ACCESSOR(DeoptimizationOutputData)
}
-SMI_ACCESSORS(FixedArray, length, kLengthOffset)
+SMI_ACCESSORS(FixedArrayBase, length, kLengthOffset)
SMI_ACCESSORS(ByteArray, length, kLengthOffset)
INT_ACCESSORS(ExternalArray, length, kLengthOffset)
return SeqTwoByteString::SizeFor(
reinterpret_cast<SeqTwoByteString*>(this)->length());
}
+ if (instance_type == FIXED_DOUBLE_ARRAY_TYPE) {
+ return FixedDoubleArray::SizeFor(
+ reinterpret_cast<FixedDoubleArray*>(this)->length());
+ }
ASSERT(instance_type == CODE_TYPE);
return reinterpret_cast<Code*>(this)->CodeSize();
}
}
Map* new_map = Map::cast(obj);
new_map->set_elements_kind(JSObject::FAST_ELEMENTS);
- isolate()->counters()->map_slow_to_fast_elements()->Increment();
+ isolate()->counters()->map_to_fast_elements()->Increment();
+ return new_map;
+}
+
+
+MaybeObject* Map::GetFastDoubleElementsMap() {
+ if (has_fast_double_elements()) return this;
+ Object* obj;
+ { MaybeObject* maybe_obj = CopyDropTransitions();
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ Map* new_map = Map::cast(obj);
+ new_map->set_elements_kind(JSObject::FAST_DOUBLE_ELEMENTS);
+ isolate()->counters()->map_to_fast_double_elements()->Increment();
return new_map;
}
MaybeObject* Map::GetSlowElementsMap() {
- if (!has_fast_elements()) return this;
+ if (!has_fast_elements() && !has_fast_double_elements()) return this;
Object* obj;
{ MaybeObject* maybe_obj = CopyDropTransitions();
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
Map* new_map = Map::cast(obj);
new_map->set_elements_kind(JSObject::DICTIONARY_ELEMENTS);
- isolate()->counters()->map_fast_to_slow_elements()->Increment();
+ isolate()->counters()->map_to_slow_elements()->Increment();
return new_map;
}
ASSERT((kind == FAST_ELEMENTS &&
(elements()->map() == GetHeap()->fixed_array_map() ||
elements()->map() == GetHeap()->fixed_cow_array_map())) ||
+ (kind == FAST_DOUBLE_ELEMENTS &&
+ elements()->IsFixedDoubleArray()) ||
(kind == DICTIONARY_ELEMENTS &&
elements()->IsFixedArray() &&
elements()->IsDictionary()) ||
}
+bool JSObject::HasFastDoubleElements() {
+ return GetElementsKind() == FAST_DOUBLE_ELEMENTS;
+}
+
+
bool JSObject::HasDictionaryElements() {
return GetElementsKind() == DICTIONARY_ELEMENTS;
}
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
case FIXED_ARRAY_TYPE:
return kVisitFixedArray;
+ case FIXED_DOUBLE_ARRAY_TYPE:
+ return kVisitFixedDoubleArray;
+
case ODDBALL_TYPE:
return kVisitOddball;
-// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
kVisitShortcutCandidate,
kVisitByteArray,
kVisitFixedArray,
+ kVisitFixedDoubleArray,
kVisitGlobalContext,
// For data objects, JS objects and structs along with generic visitor which
FixedArray::BodyDescriptor,
int>::Visit);
+ table_.Register(kVisitFixedDoubleArray, &VisitFixedDoubleArray);
+
table_.Register(kVisitGlobalContext,
&FixedBodyVisitor<StaticVisitor,
Context::ScavengeBodyDescriptor,
return reinterpret_cast<ByteArray*>(object)->ByteArraySize();
}
+ static inline int VisitFixedDoubleArray(Map* map, HeapObject* object) {
+ int length = reinterpret_cast<FixedDoubleArray*>(object)->length();
+ return FixedDoubleArray::SizeFor(length);
+ }
+
static inline int VisitSeqAsciiString(Map* map, HeapObject* object) {
return SeqAsciiString::cast(object)->
SeqAsciiStringSize(map->instance_type());
const int kGetterIndex = 0;
const int kSetterIndex = 1;
+uint64_t FixedDoubleArray::kHoleNanInt64 = -1;
+uint64_t FixedDoubleArray::kCanonicalNonHoleNanLower32 = 0x7FF00000;
+uint64_t FixedDoubleArray::kCanonicalNonHoleNanInt64 =
+ kCanonicalNonHoleNanLower32 << 32;
MUST_USE_RESULT static MaybeObject* CreateJSValue(JSFunction* constructor,
Object* value) {
case FIXED_ARRAY_TYPE:
FixedArray::BodyDescriptor::IterateBody(this, object_size, v);
break;
+ case FIXED_DOUBLE_ARRAY_TYPE:
+ break;
case JS_OBJECT_TYPE:
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_VALUE_TYPE:
ASSERT(!HasExternalArrayElements());
if (HasDictionaryElements()) return this;
Map* old_map = map();
- ASSERT(old_map->has_fast_elements());
+ ASSERT(old_map->has_fast_elements() || old_map->has_fast_double_elements());
Object* obj;
{ MaybeObject* maybe_obj = old_map->GetSlowElementsMap();
Map* new_map = Map::cast(obj);
// Get number of entries.
- FixedArray* array = FixedArray::cast(elements());
+ FixedArrayBase* array = FixedArrayBase::cast(elements());
// Compute the effective length.
int length = IsJSArray() ?
{ MaybeObject* maybe_obj = NumberDictionary::Allocate(length);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
+ bool has_double_elements = old_map->has_fast_double_elements();
NumberDictionary* dictionary = NumberDictionary::cast(obj);
// Copy entries.
for (int i = 0; i < length; i++) {
- Object* value = array->get(i);
+ Object* value = NULL;
+ if (has_double_elements) {
+ FixedDoubleArray* double_array = FixedDoubleArray::cast(array);
+ if (double_array->is_the_hole(i)) {
+ value = GetIsolate()->heap()->the_hole_value();
+ } else {
+ // 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(double_array->get(i), TENURED);
+ if (!maybe_value_object->ToObject(&value)) return maybe_value_object;
+ }
+ } else {
+ ASSERT(old_map->has_fast_elements());
+ FixedArray* fixed_array = FixedArray::cast(array);
+ value = fixed_array->get(i);
+ }
+ PropertyDetails details = PropertyDetails(NONE, NORMAL);
if (!value->IsTheHole()) {
- PropertyDetails details = PropertyDetails(NONE, NORMAL);
Object* result;
- { MaybeObject* maybe_result =
- dictionary->AddNumberEntry(i, array->get(i), details);
- if (!maybe_result->ToObject(&result)) return maybe_result;
- }
+ MaybeObject* maybe_result =
+ dictionary->AddNumberEntry(i, value, details);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
dictionary = NumberDictionary::cast(result);
}
}
{ MaybeObject* maybe_obj = EnsureWritableFastElements();
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
- uint32_t length = IsJSArray() ?
- static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
- static_cast<uint32_t>(FixedArray::cast(elements())->length());
- if (index < length) {
+ int length = IsJSArray()
+ ? Smi::cast(JSArray::cast(this)->length())->value()
+ : FixedArray::cast(elements())->length();
+ if (index < static_cast<uint32_t>(length)) {
FixedArray::cast(elements())->set_the_hole(index);
}
break;
}
+ case FAST_DOUBLE_ELEMENTS: {
+ int length = IsJSArray()
+ ? Smi::cast(JSArray::cast(this)->length())->value()
+ : FixedArray::cast(elements())->length();
+ if (index < static_cast<uint32_t>(length)) {
+ FixedDoubleArray::cast(elements())->set_the_hole(index);
+ }
+ break;
+ }
case EXTERNAL_PIXEL_ELEMENTS:
case EXTERNAL_BYTE_ELEMENTS:
case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
}
Map* new_map = Map::cast(obj);
- AssertNoAllocation no_gc;
- WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc);
switch (GetElementsKind()) {
case FAST_ELEMENTS: {
+ AssertNoAllocation no_gc;
+ WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc);
FixedArray* old_elements = FixedArray::cast(elements());
uint32_t old_length = static_cast<uint32_t>(old_elements->length());
// Fill out the new array with this content and array holes.
}
break;
}
+ case FAST_DOUBLE_ELEMENTS: {
+ FixedDoubleArray* old_elements = FixedDoubleArray::cast(elements());
+ 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(i), TENURED);
+ if (!maybe_value_object->ToObject(&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.
+ elems->set(i, obj, UPDATE_WRITE_BARRIER);
+ }
+ }
+ break;
+ }
case DICTIONARY_ELEMENTS: {
+ AssertNoAllocation no_gc;
+ WriteBarrierMode mode = elems->GetWriteBarrierMode(no_gc);
NumberDictionary* dictionary = NumberDictionary::cast(elements());
for (int i = 0; i < dictionary->Capacity(); i++) {
Object* key = dictionary->KeyAt(i);
}
+MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength(
+ int capacity,
+ int length) {
+ Heap* heap = GetHeap();
+ // We should never end in here with a pixel or external array.
+ ASSERT(!HasExternalArrayElements());
+
+ Object* obj;
+ { MaybeObject* maybe_obj =
+ heap->AllocateUninitializedFixedDoubleArray(capacity);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ FixedDoubleArray* elems = FixedDoubleArray::cast(obj);
+
+ { MaybeObject* maybe_obj = map()->GetFastDoubleElementsMap();
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ Map* new_map = Map::cast(obj);
+
+ AssertNoAllocation no_gc;
+ switch (GetElementsKind()) {
+ case FAST_ELEMENTS: {
+ elems->Initialize(FixedArray::cast(elements()));
+ break;
+ }
+ case FAST_DOUBLE_ELEMENTS: {
+ elems->Initialize(FixedDoubleArray::cast(elements()));
+ break;
+ }
+ case DICTIONARY_ELEMENTS: {
+ elems->Initialize(NumberDictionary::cast(elements()));
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ set_map(new_map);
+ set_elements(elems);
+
+ if (IsJSArray()) {
+ JSArray::cast(this)->set_length(Smi::FromInt(length));
+ }
+
+ return this;
+}
+
+
MaybeObject* JSObject::SetSlowElements(Object* len) {
// We should never end in here with a pixel or external array.
ASSERT(!HasExternalArrayElements());
if (found) return result;
}
-
// Check whether there is extra space in fixed array..
if (index < elms_length) {
elms->set(index, value);
}
+MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement(
+ uint32_t index,
+ Object* value,
+ StrictModeFlag strict_mode,
+ bool check_prototype) {
+ ASSERT(HasFastDoubleElements());
+
+ FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
+ uint32_t elms_length = static_cast<uint32_t>(elms->length());
+
+ // If storing to an element that isn't in the array, pass the store request
+ // up the prototype chain before storing in the receiver's elements.
+ if (check_prototype &&
+ (index >= elms_length || elms->is_the_hole(index))) {
+ bool found;
+ MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index,
+ value,
+ &found,
+ strict_mode);
+ if (found) return result;
+ }
+
+ // If the value object is not a heap number, switch to fast elements and try
+ // again.
+ bool value_is_smi = value->IsSmi();
+ if (!value->IsNumber()) {
+ Object* obj;
+ uint32_t length = elms_length;
+ if (IsJSArray()) {
+ CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
+ }
+ MaybeObject* maybe_obj =
+ SetFastElementsCapacityAndLength(elms_length, length);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ return SetFastElement(index, value, strict_mode, check_prototype);
+ }
+
+ double double_value = value_is_smi
+ ? static_cast<double>(Smi::cast(value)->value())
+ : HeapNumber::cast(value)->value();
+
+ // Check whether there is extra space in the fixed array.
+ if (index < elms_length) {
+ elms->set(index, double_value);
+ if (IsJSArray()) {
+ // Update the length of the array if needed.
+ uint32_t array_length = 0;
+ CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
+ if (index >= array_length) {
+ JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
+ }
+ }
+ return value;
+ }
+
+ // Allow gap in fast case.
+ if ((index - elms_length) < kMaxGap) {
+ // Try allocating extra space.
+ int new_capacity = NewElementsCapacity(index+1);
+ if (new_capacity <= kMaxFastElementsLength ||
+ !ShouldConvertToSlowElements(new_capacity)) {
+ ASSERT(static_cast<uint32_t>(new_capacity) > index);
+ Object* obj;
+ { MaybeObject* maybe_obj =
+ SetFastDoubleElementsCapacityAndLength(new_capacity,
+ index + 1);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ FixedDoubleArray::cast(elements())->set(index, double_value);
+ return value;
+ }
+ }
+
+ // Otherwise default to slow case.
+ Object* obj;
+ { MaybeObject* maybe_obj = NormalizeElements();
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ ASSERT(HasDictionaryElements());
+ return SetElement(index, value, strict_mode, check_prototype);
+}
+
+
MaybeObject* JSObject::SetElement(uint32_t index,
Object* value,
StrictModeFlag strict_mode,
case FAST_ELEMENTS:
// Fast case.
return SetFastElement(index, value, strict_mode, check_prototype);
+ case FAST_DOUBLE_ELEMENTS:
+ return SetFastDoubleElement(index, value, strict_mode, check_prototype);
case EXTERNAL_PIXEL_ELEMENTS: {
ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
return pixels->SetValue(index, value);
} else {
new_length = NumberDictionary::cast(elements())->max_number_key() + 1;
}
- Object* obj;
- { MaybeObject* maybe_obj =
- SetFastElementsCapacityAndLength(new_length, new_length);
- if (!maybe_obj->ToObject(&obj)) return maybe_obj;
- }
+ if (ShouldConvertToFastDoubleElements()) {
+ Object* obj;
+ { MaybeObject* maybe_obj =
+ SetFastDoubleElementsCapacityAndLength(new_length, new_length);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
#ifdef DEBUG
- if (FLAG_trace_normalization) {
- PrintF("Object elements are fast case again:\n");
- Print();
- }
+ if (FLAG_trace_normalization) {
+ PrintF("Object elements are fast double case again:\n");
+ Print();
+ }
#endif
+ } else {
+ Object* obj;
+ { MaybeObject* maybe_obj =
+ SetFastElementsCapacityAndLength(new_length, new_length);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+#ifdef DEBUG
+ if (FLAG_trace_normalization) {
+ PrintF("Object elements are fast case again:\n");
+ Print();
+ }
+#endif
+ }
}
return value;
}
- default:
- UNREACHABLE();
- break;
}
// All possible cases have been handled above. Add a return to avoid the
// complaints from the compiler.
}
break;
}
+ case FAST_DOUBLE_ELEMENTS: {
+ FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
+ if (index < static_cast<uint32_t>(elms->length())) {
+ if (!elms->is_the_hole(index)) {
+ return GetHeap()->NumberFromDouble(elms->get(index));
+ }
+ }
+ break;
+ }
case EXTERNAL_PIXEL_ELEMENTS:
case EXTERNAL_BYTE_ELEMENTS:
case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
}
break;
}
+ case FAST_DOUBLE_ELEMENTS: {
+ FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
+ if (index < static_cast<uint32_t>(elms->length())) {
+ if (!elms->is_the_hole(index)) {
+ double double_value = elms->get(index);
+ return GetHeap()->NumberFromDouble(double_value);
+ }
+ }
+ break;
+ }
case EXTERNAL_PIXEL_ELEMENTS:
case EXTERNAL_BYTE_ELEMENTS:
case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
}
break;
}
+ case FAST_DOUBLE_ELEMENTS:
case FAST_ELEMENTS:
case DICTIONARY_ELEMENTS:
UNREACHABLE();
}
break;
}
+ case FAST_DOUBLE_ELEMENTS: {
+ FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
+ capacity = elms->length();
+ for (int i = 0; i < capacity; i++) {
+ if (!elms->is_the_hole(i)) number_of_elements++;
+ }
+ break;
+ }
case EXTERNAL_PIXEL_ELEMENTS:
case EXTERNAL_BYTE_ELEMENTS:
case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
- ASSERT(HasFastElements());
// Keep the array in fast case if the current backing storage is
// almost filled and if the new capacity is no more than twice the
// old capacity.
- int elements_length = FixedArray::cast(elements())->length();
+ int elements_length = 0;
+ if (HasFastElements()) {
+ elements_length = FixedArray::cast(elements())->length();
+ } else if (HasFastDoubleElements()) {
+ elements_length = FixedDoubleArray::cast(elements())->length();
+ } else {
+ UNREACHABLE();
+ }
return !HasDenseElements() || ((new_capacity / 2) > elements_length);
}
}
+bool JSObject::ShouldConvertToFastDoubleElements() {
+ if (FLAG_unbox_double_arrays) {
+ ASSERT(HasDictionaryElements());
+ NumberDictionary* dictionary = NumberDictionary::cast(elements());
+ for (int i = 0; i < dictionary->Capacity(); i++) {
+ Object* key = dictionary->KeyAt(i);
+ if (key->IsNumber()) {
+ if (!dictionary->ValueAt(i)->IsNumber()) return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
// Certain compilers request function template instantiation when they
// see the definition of the other template functions in the
// class. This requires us to have the template functions put
EXTERNAL_FLOAT_ARRAY_TYPE,
EXTERNAL_DOUBLE_ARRAY_TYPE,
EXTERNAL_PIXEL_ARRAY_TYPE, // LAST_EXTERNAL_ARRAY_TYPE
+ FIXED_DOUBLE_ARRAY_TYPE,
FILLER_TYPE, // LAST_DATA_TYPE
// Structs.
V(DeoptimizationInputData) \
V(DeoptimizationOutputData) \
V(FixedArray) \
+ V(FixedDoubleArray) \
V(Context) \
V(CatchContext) \
V(GlobalContext) \
// Extract the number.
inline double Number();
+ // Returns true if the object is of the correct type to be used as a
+ // implementation of a JSObject's elements.
+ inline bool HasValidElements();
+
inline bool HasSpecificClassOf(String* name);
MUST_USE_RESULT MaybeObject* ToObject(); // ECMA-262 9.9.
// The "fast" kind for tagged values. Must be first to make it possible
// to efficiently check maps if they have fast elements.
FAST_ELEMENTS,
+
+ // The "fast" kind for unwrapped, non-tagged double values.
+ FAST_DOUBLE_ELEMENTS,
+
// The "slow" kind.
DICTIONARY_ELEMENTS,
// The "fast" kind for external arrays
MUST_USE_RESULT inline MaybeObject* ResetElements();
inline ElementsKind GetElementsKind();
inline bool HasFastElements();
+ inline bool HasFastDoubleElements();
inline bool HasDictionaryElements();
inline bool HasExternalPixelElements();
inline bool HasExternalArrayElements();
// storage would. In that case the JSObject should have fast
// elements.
bool ShouldConvertToFastElements();
+ // Returns true if the elements of JSObject contains only values that can be
+ // represented in a FixedDoubleArray.
+ bool ShouldConvertToFastDoubleElements();
// Tells whether the index'th element is present.
inline bool HasElement(uint32_t index);
StrictModeFlag strict_mode,
bool check_prototype = true);
+ MUST_USE_RESULT MaybeObject* SetFastDoubleElement(
+ uint32_t index,
+ Object* value,
+ StrictModeFlag strict_mode,
+ bool check_prototype = true);
+
// Set the index'th array element.
// A Failure object is returned if GC is needed.
MUST_USE_RESULT MaybeObject* SetElement(uint32_t index,
MUST_USE_RESULT MaybeObject* SetFastElementsCapacityAndLength(int capacity,
int length);
+ MUST_USE_RESULT MaybeObject* SetFastDoubleElementsCapacityAndLength(
+ int capacity,
+ int length);
MUST_USE_RESULT MaybeObject* SetSlowElements(Object* length);
// Lookup interceptors are used for handling properties controlled by host
};
-// FixedArray describes fixed-sized arrays with element type Object*.
-class FixedArray: public HeapObject {
+// Common superclass for FixedArrays that allow implementations to share
+// common accessors and some code paths.
+class FixedArrayBase: public HeapObject {
public:
// [length]: length of the array.
inline int length();
inline void set_length(int value);
+ inline static FixedArrayBase* cast(Object* object);
+
+ // Layout description.
+ // Length is smi tagged when it is stored.
+ static const int kLengthOffset = HeapObject::kHeaderSize;
+ static const int kHeaderSize = kLengthOffset + kPointerSize;
+};
+
+
+// FixedArray describes fixed-sized arrays with element type Object*.
+class FixedArray: public FixedArrayBase {
+ public:
// Setter and getter for elements.
inline Object* get(int index);
// Setter that uses write barrier.
// Casting.
static inline FixedArray* cast(Object* obj);
- // Layout description.
- // Length is smi tagged when it is stored.
- static const int kLengthOffset = HeapObject::kHeaderSize;
- static const int kHeaderSize = kLengthOffset + kPointerSize;
-
// Maximal allowed size, in bytes, of a single FixedArray.
// Prevents overflowing size computations, as well as extreme memory
// consumption.
};
+// FixedDoubleArray describes fixed-sized arrays with element type double.
+class FixedDoubleArray: public FixedArrayBase {
+ public:
+ inline void Initialize(FixedArray* from);
+ inline void Initialize(FixedDoubleArray* from);
+ inline void Initialize(NumberDictionary* from);
+
+ // Setter and getter for elements.
+ inline double get(int index);
+ inline void set(int index, double value);
+ inline void set_the_hole(int index);
+
+ // Checking for the hole.
+ inline bool is_the_hole(int index);
+
+ // Garbage collection support.
+ inline static int SizeFor(int length) {
+ return kHeaderSize + length * kDoubleSize;
+ }
+
+ // The following can't be declared inline as const static
+ // because they're 64-bit.
+ static uint64_t kCanonicalNonHoleNanLower32;
+ static uint64_t kCanonicalNonHoleNanInt64;
+ static uint64_t kHoleNanInt64;
+
+ inline static bool is_the_hole_nan(double value) {
+ return BitCast<uint64_t, double>(value) == kHoleNanInt64;
+ }
+
+ inline static double hole_nan_as_double() {
+ return BitCast<double, uint64_t>(kHoleNanInt64);
+ }
+
+ inline static double canonical_not_the_hole_nan_as_double() {
+ return BitCast<double, uint64_t>(kCanonicalNonHoleNanInt64);
+ }
+
+ // Casting.
+ static inline FixedDoubleArray* cast(Object* obj);
+
+ // Maximal allowed size, in bytes, of a single FixedDoubleArray.
+ // Prevents overflowing size computations, as well as extreme memory
+ // consumption.
+ static const int kMaxSize = 512 * MB;
+ // Maximally allowed length of a FixedArray.
+ static const int kMaxLength = (kMaxSize - kHeaderSize) / kDoubleSize;
+
+ // Dispatched behavior.
+#ifdef OBJECT_PRINT
+ inline void FixedDoubleArrayPrint() {
+ FixedDoubleArrayPrint(stdout);
+ }
+ void FixedDoubleArrayPrint(FILE* out);
+#endif
+
+#ifdef DEBUG
+ void FixedDoubleArrayVerify();
+#endif
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FixedDoubleArray);
+};
+
+
// DescriptorArrays are fixed arrays used to hold instance descriptors.
// The format of the these objects is:
// TODO(1399): It should be possible to make room for bit_field3 in the map
return elements_kind() == JSObject::FAST_ELEMENTS;
}
+ inline bool has_fast_double_elements() {
+ return elements_kind() == JSObject::FAST_DOUBLE_ELEMENTS;
+ }
+
inline bool has_external_array_elements() {
JSObject::ElementsKind kind(elements_kind());
return kind >= JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND &&
// instance descriptors.
MUST_USE_RESULT MaybeObject* CopyDropTransitions();
- // Returns this map if it has the fast elements bit set, otherwise
+ // Returns this map if it already has elements that are fast, otherwise
// returns a copy of the map, with all transitions dropped from the
- // descriptors and the fast elements bit set.
+ // descriptors and the ElementsKind set to FAST_ELEMENTS.
MUST_USE_RESULT inline MaybeObject* GetFastElementsMap();
- // Returns this map if it has the fast elements bit cleared,
- // otherwise returns a copy of the map, with all transitions dropped
- // from the descriptors and the fast elements bit cleared.
+ // Returns this map if it already has fast elements that are doubles,
+ // otherwise returns a copy of the map, with all transitions dropped from the
+ // descriptors and the ElementsKind set to FAST_DOUBLE_ELEMENTS.
+ MUST_USE_RESULT inline MaybeObject* GetFastDoubleElementsMap();
+
+ // Returns this map if already has dictionary elements, otherwise returns a
+ // copy of the map, with all transitions dropped from the descriptors and the
+ // ElementsKind set to DICTIONARY_ELEMENTS.
MUST_USE_RESULT inline MaybeObject* GetSlowElementsMap();
// Returns a new map with all transitions dropped from the descriptors and the
- // external array elements bit set.
+ // ElementsKind set to one of the value corresponding to array_type.
MUST_USE_RESULT MaybeObject* GetExternalArrayElementsMap(
ExternalArrayType array_type,
bool safe_to_add_transition);
// strings), fixed arrays, and byte arrays in large object space.
ASSERT(object->IsCode() || object->IsSeqString() ||
object->IsExternalString() || object->IsFixedArray() ||
- object->IsByteArray());
+ object->IsFixedDoubleArray() || object->IsByteArray());
// The object itself should look OK.
object->Verify();
V8.GCCompactorCausedByWeakHandles) \
SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \
SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \
- SC(map_slow_to_fast_elements, V8.MapSlowToFastElements) \
- SC(map_fast_to_slow_elements, V8.MapFastToSlowElements) \
+ SC(map_to_fast_elements, V8.MapToFastElements) \
+ SC(map_to_fast_double_elements, V8.MapToFastDoubleElements) \
+ SC(map_to_slow_elements, V8.MapToSlowElements) \
SC(map_to_external_array_elements, V8.MapToExternalArrayElements) \
/* How is the generic keyed-load stub used? */ \
SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \
--- /dev/null
+// Copyright 2011 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.
+
+// Test dictionary -> double elements -> dictionary elements round trip
+
+var foo = new Array(500000);
+
+function func(a) {
+ for (var i= 0; i < 100000; ++i ) {
+ a[i] = i+0.5;
+ }
+}
+
+func(foo);
+
+for (var i= 0; i < 100000; i += 500 ) {
+ assertEquals(i+0.5, foo[i]);
+}
+
+delete foo[5];
+// Don't use assertEquals for comparison to undefined due to
+assertTrue(undefined === foo[5]);
+assertTrue(undefined === foo[500000-1]);
+assertTrue(undefined === foo[-1]);
+assertEquals(500000, foo.length);
+
+// Cause the array to grow beyond it's JSArray length. This will double the
+// size of the capacity and force the array into "slow" dictionary case.
+foo[500001] = 50;
+assertEquals(50, foo[500001]);
+assertEquals(500002, foo.length);
+assertTrue(undefined === foo[5])
+assertTrue(undefined === foo[500000-1])
+assertTrue(undefined === foo[-1])
+assertEquals(500002, foo.length);
+
+// Test dictionary -> double elements -> fast elements.
+
+var foo2 = new Array(500000);
+func(foo2);
+delete foo2[5];
+
+// Convert back to fast elements and make sure the contents of the array are
+// unchanged.
+foo2[25] = new Object();
+for (var i= 0; i < 100000; i += 500 ) {
+ if (i != 25 && i != 5) {
+ assertEquals(i+0.5, foo2[i]);
+ }
+}
+assertTrue(undefined === foo2[5])
+assertTrue(undefined === foo2[500000-1])
+assertTrue(undefined === foo2[-1])
+assertEquals(500000, foo2.length);