Commit 4de3bb50 authored by danno@chromium.org's avatar danno@chromium.org

Implement core support for FixedDoubleArrays.

Under a flag without IC or Crankshaft support.

BUG=none
TEST=none

Review URL: http://codereview.chromium.org/7089002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8229 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 62a65fcc
......@@ -3726,7 +3726,7 @@ class Internals {
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:
......@@ -58,6 +58,16 @@ Handle<FixedArray> Factory::NewFixedArrayWithHoles(int size,
}
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:
......@@ -39,7 +39,7 @@ namespace internal {
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);
......@@ -49,6 +49,11 @@ class Factory {
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);
......
......@@ -99,6 +99,9 @@ private:
// 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")
......
......@@ -1286,6 +1286,7 @@ class ScavengingVisitor : public StaticVisitorBase {
table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate);
table_.Register(kVisitByteArray, &EvacuateByteArray);
table_.Register(kVisitFixedArray, &EvacuateFixedArray);
table_.Register(kVisitFixedDoubleArray, &EvacuateFixedDoubleArray);
table_.Register(kVisitGlobalContext,
&ObjectEvacuationStrategy<POINTER_OBJECT>::
......@@ -1433,6 +1434,18 @@ class ScavengingVisitor : public StaticVisitorBase {
}
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) {
......@@ -1771,6 +1784,12 @@ bool Heap::CreateInitialMaps() {
set_undetectable_ascii_string_map(Map::cast(obj));
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;
......@@ -3812,6 +3831,62 @@ MaybeObject* Heap::AllocateUninitializedFixedArray(int length) {
}
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);
......
......@@ -66,6 +66,7 @@ inline Heap* _inline_get_heap_();
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) \
......@@ -78,6 +79,7 @@ inline Heap* _inline_get_heap_();
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) \
......@@ -620,6 +622,17 @@ class Heap {
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(
......@@ -1460,6 +1473,9 @@ class Heap {
// 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.
......
......@@ -396,6 +396,8 @@ class StaticMarkingVisitor : public StaticVisitorBase {
FixedArray::BodyDescriptor,
void>::Visit);
table_.Register(kVisitFixedDoubleArray, DataObjectVisitor::Visit);
table_.Register(kVisitGlobalContext,
&FixedBodyVisitor<StaticMarkingVisitor,
Context::MarkCompactBodyDescriptor,
......
......@@ -88,6 +88,9 @@ void HeapObject::HeapObjectVerify() {
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;
......@@ -307,6 +310,17 @@ void FixedArray::FixedArrayVerify() {
}
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()) {
......@@ -432,7 +446,9 @@ void Code::CodeVerify() {
void JSArray::JSArrayVerify() {
JSObjectVerify();
ASSERT(length()->IsNumber() || length()->IsUndefined());
ASSERT(elements()->IsUndefined() || elements()->IsFixedArray());
ASSERT(elements()->IsUndefined() ||
elements()->IsFixedArray() ||
elements()->IsFixedDoubleArray());
}
......
......@@ -217,6 +217,10 @@ bool Object::IsExternalTwoByteString() {
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()) {
......@@ -489,6 +493,13 @@ bool Object::IsFixedArray() {
}
bool Object::IsFixedDoubleArray() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() ==
FIXED_DOUBLE_ARRAY_TYPE;
}
bool Object::IsDescriptorArray() {
return IsFixedArray();
}
......@@ -1318,8 +1329,7 @@ ACCESSORS(JSObject, properties, FixedArray, kPropertiesOffset)
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);
}
......@@ -1328,8 +1338,7 @@ void JSObject::set_elements(HeapObject* value, WriteBarrierMode mode) {
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);
}
......@@ -1577,6 +1586,12 @@ bool Object::IsStringObjectWithCharacterAt(uint32_t index) {
}
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);
......@@ -1600,6 +1615,88 @@ void FixedArray::set(int index, Object* value) {
}
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;
......@@ -1900,6 +1997,7 @@ void NumberDictionary::set_requires_slow_elements() {
CAST_ACCESSOR(FixedArray)
CAST_ACCESSOR(FixedDoubleArray)
CAST_ACCESSOR(DescriptorArray)
CAST_ACCESSOR(DeoptimizationInputData)
CAST_ACCESSOR(DeoptimizationOutputData)
......@@ -1964,7 +2062,7 @@ HashTable<Shape, Key>* HashTable<Shape, Key>::cast(Object* obj) {
}
SMI_ACCESSORS(FixedArray, length, kLengthOffset)
SMI_ACCESSORS(FixedArrayBase, length, kLengthOffset)
SMI_ACCESSORS(ByteArray, length, kLengthOffset)
INT_ACCESSORS(ExternalArray, length, kLengthOffset)
......@@ -2423,6 +2521,10 @@ int HeapObject::SizeFromMap(Map* map) {
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();
}
......@@ -2981,20 +3083,33 @@ MaybeObject* Map::GetFastElementsMap() {
}
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;
}
......@@ -3788,6 +3903,8 @@ JSObject::ElementsKind JSObject::GetElementsKind() {
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()) ||
......@@ -3801,6 +3918,11 @@ bool JSObject::HasFastElements() {
}
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:
......@@ -73,6 +73,9 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
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:
......@@ -52,6 +52,7 @@ class StaticVisitorBase : public AllStatic {
kVisitShortcutCandidate,
kVisitByteArray,
kVisitFixedArray,
kVisitFixedDoubleArray,
kVisitGlobalContext,
// For data objects, JS objects and structs along with generic visitor which
......@@ -285,6 +286,8 @@ class StaticNewSpaceVisitor : public StaticVisitorBase {
FixedArray::BodyDescriptor,
int>::Visit);
table_.Register(kVisitFixedDoubleArray, &VisitFixedDoubleArray);
table_.Register(kVisitGlobalContext,
&FixedBodyVisitor<StaticVisitor,
Context::ScavengeBodyDescriptor,
......@@ -329,6 +332,11 @@ class StaticNewSpaceVisitor : public StaticVisitorBase {
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());
......
......@@ -58,6 +58,10 @@ namespace internal {
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) {
......@@ -1178,6 +1182,8 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
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:
......@@ -2800,7 +2806,7 @@ MaybeObject* JSObject::NormalizeElements() {
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();
......@@ -2809,7 +2815,7 @@ MaybeObject* JSObject::NormalizeElements() {
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() ?
......@@ -2818,17 +2824,35 @@ MaybeObject* JSObject::NormalizeElements() {
{ 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);
}
}
......@@ -2998,14 +3022,23 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
{ 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:
......@@ -7098,10 +7131,10 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
}
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.
......@@ -7110,7 +7143,32 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
}
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);
......@@ -7137,6 +7195,55 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
}
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());
......@@ -7858,7 +7965,6 @@ MaybeObject* JSObject::SetFastElement(uint32_t index,
if (found) return result;
}
// Check whether there is extra space in fixed array..
if (index < elms_length) {
elms->set(index, value);
......@@ -7900,6 +8006,89 @@ MaybeObject* JSObject::SetFastElement(uint32_t index,
}
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,
......@@ -7949,6 +8138,8 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
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);
......@@ -8072,24 +8263,35 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
} 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.
......@@ -8129,6 +8331,15 @@ MaybeObject* JSObject::GetElementPostInterceptor(Object* receiver,
}
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:
......@@ -8232,6 +8443,16 @@ MaybeObject* JSObject::GetElementWithReceiver(Object* receiver,
}
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:
......@@ -8351,6 +8572,7 @@ MaybeObject* JSObject::GetExternalElement(uint32_t index) {
}
break;
}
case FAST_DOUBLE_ELEMENTS:
case FAST_ELEMENTS:
case DICTIONARY_ELEMENTS:
UNREACHABLE();
......@@ -8373,6 +8595,14 @@ bool JSObject::HasDenseElements() {
}
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:
......@@ -8401,11 +8631,17 @@ bool JSObject::HasDenseElements() {
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);
}
......@@ -8435,6 +8671,23 @@ bool JSObject::ShouldConvertToFastElements() {
}
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
......
......@@ -533,6 +533,7 @@ enum InstanceType {
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.
......@@ -732,6 +733,7 @@ class MaybeObject BASE_EMBEDDED {
V(DeoptimizationInputData) \
V(DeoptimizationOutputData) \
V(FixedArray) \
V(FixedDoubleArray) \
V(Context) \
V(CatchContext) \
V(GlobalContext) \
......@@ -800,6 +802,10 @@ class Object : public MaybeObject {
// 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.
......@@ -1427,6 +1433,10 @@ class JSObject: public JSReceiver {
// 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
......@@ -1478,6 +1488,7 @@ class JSObject: public JSReceiver {
MUST_USE_RESULT inline MaybeObject* ResetElements();
inline ElementsKind GetElementsKind();
inline bool HasFastElements();
inline bool HasFastDoubleElements();
inline bool HasDictionaryElements();
inline bool HasExternalPixelElements();
inline bool HasExternalArrayElements();
......@@ -1637,6 +1648,9 @@ class JSObject: public JSReceiver {
// 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);
......@@ -1676,6 +1690,12 @@ class JSObject: public JSReceiver {
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,
......@@ -1695,6 +1715,9 @@ class JSObject: public JSReceiver {
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
......@@ -1986,13 +2009,26 @@ class JSObject: public JSReceiver {
};
// 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.
......@@ -2043,11 +2079,6 @@ class FixedArray: public HeapObject {
// 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.
......@@ -2095,6 +2126,71 @@ class FixedArray: public HeapObject {
};
// 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
......@@ -3809,6 +3905,10 @@ class Map: public HeapObject {
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 &&
......@@ -3901,18 +4001,23 @@ class Map: public HeapObject {
// 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);
......
......@@ -3070,7 +3070,7 @@ void LargeObjectSpace::Verify() {
// 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();
......
......@@ -126,8 +126,9 @@ namespace internal {
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) \
......
// 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);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment