Commit 2d9bfe9a authored by jwolfe's avatar jwolfe Committed by Commit bot

fix Set::AsArray to not leave undefined holes in output array

Add comments explaining how to iterate over an OrderedHashTable.
Use the correct strategy for iteration in Set::AsArray().
Add a DCHECK bounds check in OrderedHashTable::KeyAt().

BUG=v8:4946
LOG=y

Review-Url: https://codereview.chromium.org/1952093002
Cr-Commit-Position: refs/heads/master@{#36091}
parent 643dfe0d
......@@ -6441,14 +6441,18 @@ Local<Array> Set::AsArray() const {
LOG_API(isolate, "Set::AsArray");
ENTER_V8(isolate);
i::Handle<i::OrderedHashSet> table(i::OrderedHashSet::cast(obj->table()));
int capacity = table->UsedCapacity();
int length = table->NumberOfElements();
i::Handle<i::FixedArray> result = factory->NewFixedArray(length);
for (int i = 0; i < length; ++i) {
int result_index = 0;
for (int i = 0; i < capacity; ++i) {
i::Object* key = table->KeyAt(i);
if (!key->IsTheHole()) {
result->set(i, key);
result->set(result_index++, key);
}
}
DCHECK_EQ(result_index, result->length());
DCHECK_EQ(result_index, length);
i::Handle<i::JSArray> result_array =
factory->NewJSArrayWithElements(result, i::FAST_ELEMENTS, length);
return Utils::ToLocal(result_array);
......
......@@ -3825,7 +3825,7 @@ class OrderedHashTable: public FixedArray {
static Handle<Derived> Shrink(Handle<Derived> table);
// Returns a new empty OrderedHashTable and records the clearing so that
// exisiting iterators can be updated.
// existing iterators can be updated.
static Handle<Derived> Clear(Handle<Derived> table);
// Returns a true if the OrderedHashTable contains the key
......@@ -3839,6 +3839,8 @@ class OrderedHashTable: public FixedArray {
return Smi::cast(get(kNumberOfDeletedElementsIndex))->value();
}
// Returns the number of contiguous entries in the data table, starting at 0,
// that either are real entries or have been deleted.
int UsedCapacity() { return NumberOfElements() + NumberOfDeletedElements(); }
int NumberOfBuckets() {
......@@ -3870,7 +3872,11 @@ class OrderedHashTable: public FixedArray {
return Smi::cast(next_entry)->value();
}
Object* KeyAt(int entry) { return get(EntryToIndex(entry)); }
// use KeyAt(i)->IsTheHole() to determine if this is a deleted entry.
Object* KeyAt(int entry) {
DCHECK_LT(entry, this->UsedCapacity());
return get(EntryToIndex(entry));
}
bool IsObsolete() {
return !get(kNextTableIndex)->IsSmi();
......@@ -3931,6 +3937,7 @@ class OrderedHashTable: public FixedArray {
set(kNumberOfDeletedElementsIndex, Smi::FromInt(num));
}
// Returns the number elements that can fit into the allocated buffer.
int Capacity() {
return NumberOfBuckets() * kLoadFactor;
}
......
......@@ -24574,6 +24574,32 @@ TEST(Set) {
CHECK_EQ(0U, set->Size());
}
TEST(SetDeleteThenAsArray) {
// https://bugs.chromium.org/p/v8/issues/detail?id=4946
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
LocalContext env;
// make an array
v8::Local<v8::Value> val = CompileRun("new Set([1, 2, 3])");
v8::Local<v8::Set> set = v8::Local<v8::Set>::Cast(val);
CHECK_EQ(3U, set->Size());
// delete the "middle" element (using AsArray to
// determine which element is the "middle" element)
v8::Local<v8::Array> array1 = set->AsArray();
CHECK_EQ(3U, array1->Length());
CHECK(set->Delete(env.local(), array1->Get(env.local(), 1).ToLocalChecked())
.FromJust());
// make sure there are no undefined values when we convert to an array again.
v8::Local<v8::Array> array2 = set->AsArray();
uint32_t length = array2->Length();
CHECK_EQ(2U, length);
for (uint32_t i = 0; i < length; i++) {
CHECK(!array2->Get(env.local(), i).ToLocalChecked()->IsUndefined());
}
}
TEST(CompatibleReceiverCheckOnCachedICHandler) {
v8::Isolate* isolate = CcTest::isolate();
......
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