Skip to content

Commit

Permalink
Fix preview of set entries
Browse files Browse the repository at this point in the history
Set entries return an array with the value as first and second entry.
As such these are considered key value pairs to align with maps
entries iterator.
So far the return value was identical to the values iterator and that
is misleading.

This also adds tests to verify the results and improves the coverage
a tiny bit by testing different iterators.

Refs: nodejs/node#24629

[email protected]

Change-Id: I669a724bb4afaf5a713e468b1f51691d22c25253
Reviewed-on: https://chromium-review.googlesource.com/c/1350790
Commit-Queue: Yang Guo <[email protected]>
Reviewed-by: Benedikt Meurer <[email protected]>
Reviewed-by: Jakob Gruber <[email protected]>
Reviewed-by: Yang Guo <[email protected]>
Cr-Commit-Position: refs/heads/master@{#59311}
  • Loading branch information
BridgeAR authored and Commit Bot committed Feb 3, 2019
1 parent 58d5361 commit 74571c8
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 29 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ Rick Waldron <[email protected]>
Rob Wu <[email protected]>
Robert Mustacchi <[email protected]>
Robert Nagy <[email protected]>
Ruben Bridgewater <[email protected]>
Ryan Dahl <[email protected]>
Sakthipriyan Vairamani (thefourtheye) <[email protected]>
Sander Mathijs van Veen <[email protected]>
Expand Down
31 changes: 20 additions & 11 deletions src/api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6985,6 +6985,11 @@ enum class MapAsArrayKind {
kValues = i::JS_MAP_VALUE_ITERATOR_TYPE
};

enum class SetAsArrayKind {
kEntries = i::JS_SET_KEY_VALUE_ITERATOR_TYPE,
kValues = i::JS_SET_VALUE_ITERATOR_TYPE
};

i::Handle<i::JSArray> MapAsArray(i::Isolate* isolate, i::Object table_obj,
int offset, MapAsArrayKind kind) {
i::Factory* factory = isolate->factory();
Expand Down Expand Up @@ -7094,13 +7099,14 @@ Maybe<bool> Set::Delete(Local<Context> context, Local<Value> key) {

namespace {
i::Handle<i::JSArray> SetAsArray(i::Isolate* isolate, i::Object table_obj,
int offset) {
int offset, SetAsArrayKind kind) {
i::Factory* factory = isolate->factory();
i::Handle<i::OrderedHashSet> table(i::OrderedHashSet::cast(table_obj),
isolate);
// Elements skipped by |offset| may already be deleted.
int capacity = table->UsedCapacity();
int max_length = capacity - offset;
const bool collect_key_values = kind == SetAsArrayKind::kEntries;
int max_length = (capacity - offset) * (collect_key_values ? 2 : 1);
if (max_length == 0) return factory->NewJSArray(0);
i::Handle<i::FixedArray> result = factory->NewFixedArray(max_length);
int result_index = 0;
Expand All @@ -7111,6 +7117,7 @@ i::Handle<i::JSArray> SetAsArray(i::Isolate* isolate, i::Object table_obj,
i::Object key = table->KeyAt(i);
if (key == the_hole) continue;
result->set(result_index++, key);
if (collect_key_values) result->set(result_index++, key);
}
}
DCHECK_GE(max_length, result_index);
Expand All @@ -7126,7 +7133,8 @@ Local<Array> Set::AsArray() const {
i::Isolate* isolate = obj->GetIsolate();
LOG_API(isolate, Set, AsArray);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
return Utils::ToLocal(SetAsArray(isolate, obj->table(), 0));
return Utils::ToLocal(
SetAsArray(isolate, obj->table(), 0, SetAsArrayKind::kValues));
}


Expand Down Expand Up @@ -9553,21 +9561,22 @@ v8::MaybeLocal<v8::Array> v8::Object::PreviewEntries(bool* is_key_value) {
i::Handle<i::JSWeakCollection>::cast(object), 0));
}
if (object->IsJSMapIterator()) {
i::Handle<i::JSMapIterator> iterator =
i::Handle<i::JSMapIterator>::cast(object);
i::Handle<i::JSMapIterator> it = i::Handle<i::JSMapIterator>::cast(object);
MapAsArrayKind const kind =
static_cast<MapAsArrayKind>(iterator->map()->instance_type());
static_cast<MapAsArrayKind>(it->map()->instance_type());
*is_key_value = kind == MapAsArrayKind::kEntries;
if (!iterator->HasMore()) return v8::Array::New(v8_isolate);
return Utils::ToLocal(MapAsArray(isolate, iterator->table(),
i::Smi::ToInt(iterator->index()), kind));
if (!it->HasMore()) return v8::Array::New(v8_isolate);
return Utils::ToLocal(
MapAsArray(isolate, it->table(), i::Smi::ToInt(it->index()), kind));
}
if (object->IsJSSetIterator()) {
i::Handle<i::JSSetIterator> it = i::Handle<i::JSSetIterator>::cast(object);
*is_key_value = false;
SetAsArrayKind const kind =
static_cast<SetAsArrayKind>(it->map()->instance_type());
*is_key_value = kind == SetAsArrayKind::kEntries;
if (!it->HasMore()) return v8::Array::New(v8_isolate);
return Utils::ToLocal(
SetAsArray(isolate, it->table(), i::Smi::ToInt(it->index())));
SetAsArray(isolate, it->table(), i::Smi::ToInt(it->index()), kind));
}
return v8::MaybeLocal<v8::Array>();
}
Expand Down
230 changes: 228 additions & 2 deletions test/cctest/test-api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28649,7 +28649,7 @@ TEST(MicrotaskContextShouldBeNativeContext) {
isolate->RunMicrotasks();
}

TEST(PreviewSetIteratorEntriesWithDeleted) {
TEST(PreviewSetKeysIteratorEntriesWithDeleted) {
LocalContext env;
v8::HandleScope handle_scope(env->GetIsolate());
v8::Local<v8::Context> context = env.local();
Expand Down Expand Up @@ -28748,7 +28748,142 @@ TEST(PreviewSetIteratorEntriesWithDeleted) {
}
}

TEST(PreviewMapIteratorEntriesWithDeleted) {
TEST(PreviewSetValuesIteratorEntriesWithDeleted) {
LocalContext env;
v8::HandleScope handle_scope(env->GetIsolate());
v8::Local<v8::Context> context = env.local();

{
// Create set, delete entry, create iterator, preview.
v8::Local<v8::Object> iterator =
CompileRun("var set = new Set([1,2,3]); set.delete(1); set.values()")
->ToObject(context)
.ToLocalChecked();
bool is_key;
v8::Local<v8::Array> entries =
iterator->PreviewEntries(&is_key).ToLocalChecked();
CHECK(!is_key);
CHECK_EQ(2, entries->Length());
CHECK_EQ(2, entries->Get(context, 0)
.ToLocalChecked()
->Int32Value(context)
.FromJust());
CHECK_EQ(3, entries->Get(context, 1)
.ToLocalChecked()
->Int32Value(context)
.FromJust());
}
{
// Create set, create iterator, delete entry, preview.
v8::Local<v8::Object> iterator =
CompileRun("var set = new Set([1,2,3]); set.values()")
->ToObject(context)
.ToLocalChecked();
CompileRun("set.delete(1);");
bool is_key;
v8::Local<v8::Array> entries =
iterator->PreviewEntries(&is_key).ToLocalChecked();
CHECK(!is_key);
CHECK_EQ(2, entries->Length());
CHECK_EQ(2, entries->Get(context, 0)
.ToLocalChecked()
->Int32Value(context)
.FromJust());
CHECK_EQ(3, entries->Get(context, 1)
.ToLocalChecked()
->Int32Value(context)
.FromJust());
}
{
// Create set, create iterator, delete entry, iterate, preview.
v8::Local<v8::Object> iterator =
CompileRun("var set = new Set([1,2,3]); var it = set.values(); it")
->ToObject(context)
.ToLocalChecked();
CompileRun("set.delete(1); it.next();");
bool is_key;
v8::Local<v8::Array> entries =
iterator->PreviewEntries(&is_key).ToLocalChecked();
CHECK(!is_key);
CHECK_EQ(1, entries->Length());
CHECK_EQ(3, entries->Get(context, 0)
.ToLocalChecked()
->Int32Value(context)
.FromJust());
}
{
// Create set, create iterator, delete entry, iterate until empty, preview.
v8::Local<v8::Object> iterator =
CompileRun("var set = new Set([1,2,3]); var it = set.values(); it")
->ToObject(context)
.ToLocalChecked();
CompileRun("set.delete(1); it.next(); it.next();");
bool is_key;
v8::Local<v8::Array> entries =
iterator->PreviewEntries(&is_key).ToLocalChecked();
CHECK(!is_key);
CHECK_EQ(0, entries->Length());
}
{
// Create set, create iterator, delete entry, iterate, trigger rehash,
// preview.
v8::Local<v8::Object> iterator =
CompileRun("var set = new Set([1,2,3]); var it = set.values(); it")
->ToObject(context)
.ToLocalChecked();
CompileRun("set.delete(1); it.next();");
CompileRun("for (var i = 4; i < 20; i++) set.add(i);");
bool is_key;
v8::Local<v8::Array> entries =
iterator->PreviewEntries(&is_key).ToLocalChecked();
CHECK(!is_key);
CHECK_EQ(17, entries->Length());
for (uint32_t i = 0; i < 17; i++) {
CHECK_EQ(i + 3, entries->Get(context, i)
.ToLocalChecked()
->Int32Value(context)
.FromJust());
}
}
}

TEST(PreviewMapEntriesIteratorEntries) {
LocalContext env;
v8::HandleScope handle_scope(env->GetIsolate());
v8::Local<v8::Context> context = env.local();
{
// Create set, delete entry, create entries iterator, preview.
v8::Local<v8::Object> iterator =
CompileRun("var set = new Set([1,2,3]); set.delete(2); set.entries()")
->ToObject(context)
.ToLocalChecked();
bool is_key;
v8::Local<v8::Array> entries =
iterator->PreviewEntries(&is_key).ToLocalChecked();
CHECK(is_key);
CHECK_EQ(4, entries->Length());
uint32_t first = entries->Get(context, 0)
.ToLocalChecked()
->Int32Value(context)
.FromJust();
uint32_t second = entries->Get(context, 2)
.ToLocalChecked()
->Int32Value(context)
.FromJust();
CHECK_EQ(1, first);
CHECK_EQ(3, second);
CHECK_EQ(first, entries->Get(context, 1)
.ToLocalChecked()
->Int32Value(context)
.FromJust());
CHECK_EQ(second, entries->Get(context, 3)
.ToLocalChecked()
->Int32Value(context)
.FromJust());
}
}

TEST(PreviewMapValuesIteratorEntriesWithDeleted) {
LocalContext env;
v8::HandleScope handle_scope(env->GetIsolate());
v8::Local<v8::Context> context = env.local();
Expand Down Expand Up @@ -28863,6 +28998,97 @@ TEST(PreviewMapIteratorEntriesWithDeleted) {
}
}

TEST(PreviewMapKeysIteratorEntriesWithDeleted) {
LocalContext env;
v8::HandleScope handle_scope(env->GetIsolate());
v8::Local<v8::Context> context = env.local();

{
// Create map, delete entry, create iterator, preview.
v8::Local<v8::Object> iterator = CompileRun(
"var map = new Map();"
"var key = 1; map.set(key, {});"
"map.set(2, {}); map.set(3, {});"
"map.delete(key);"
"map.keys()")
->ToObject(context)
.ToLocalChecked();
bool is_key;
v8::Local<v8::Array> entries =
iterator->PreviewEntries(&is_key).ToLocalChecked();
CHECK(!is_key);
CHECK_EQ(2, entries->Length());
CHECK_EQ(2, entries->Get(context, 0)
.ToLocalChecked()
->Int32Value(context)
.FromJust());
CHECK_EQ(3, entries->Get(context, 1)
.ToLocalChecked()
->Int32Value(context)
.FromJust());
}
{
// Create map, create iterator, delete entry, preview.
v8::Local<v8::Object> iterator = CompileRun(
"var map = new Map();"
"var key = 1; map.set(key, {});"
"map.set(2, {}); map.set(3, {});"
"map.keys()")
->ToObject(context)
.ToLocalChecked();
CompileRun("map.delete(key);");
bool is_key;
v8::Local<v8::Array> entries =
iterator->PreviewEntries(&is_key).ToLocalChecked();
CHECK(!is_key);
CHECK_EQ(2, entries->Length());
CHECK_EQ(2, entries->Get(context, 0)
.ToLocalChecked()
->Int32Value(context)
.FromJust());
CHECK_EQ(3, entries->Get(context, 1)
.ToLocalChecked()
->Int32Value(context)
.FromJust());
}
{
// Create map, create iterator, delete entry, iterate, preview.
v8::Local<v8::Object> iterator = CompileRun(
"var map = new Map();"
"var key = 1; map.set(key, {});"
"map.set(2, {}); map.set(3, {});"
"var it = map.keys(); it")
->ToObject(context)
.ToLocalChecked();
CompileRun("map.delete(key); it.next();");
bool is_key;
v8::Local<v8::Array> entries =
iterator->PreviewEntries(&is_key).ToLocalChecked();
CHECK(!is_key);
CHECK_EQ(1, entries->Length());
CHECK_EQ(3, entries->Get(context, 0)
.ToLocalChecked()
->Int32Value(context)
.FromJust());
}
{
// Create map, create iterator, delete entry, iterate until empty, preview.
v8::Local<v8::Object> iterator = CompileRun(
"var map = new Map();"
"var key = 1; map.set(key, {});"
"map.set(2, {}); map.set(3, {});"
"var it = map.keys(); it")
->ToObject(context)
.ToLocalChecked();
CompileRun("map.delete(key); it.next(); it.next();");
bool is_key;
v8::Local<v8::Array> entries =
iterator->PreviewEntries(&is_key).ToLocalChecked();
CHECK(!is_key);
CHECK_EQ(0, entries->Length());
}
}

namespace {
static v8::Isolate* isolate_1;
static v8::Isolate* isolate_2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,27 +166,39 @@ expression: (new Map([[1,2]])).entries()
}
]

expression: (new Set([[1,2]])).entries()
expression: (new Set([1,2])).entries()
[[Entries]]:
[
[0] : {
key : {
description : 1
overflow : false
properties : [
]
type : number
}
value : {
description : Array(2)
description : 1
overflow : false
properties : [
[0] : {
name : 0
type : number
value : 1
}
[1] : {
name : 1
type : number
value : 2
}
]
subtype : array
type : object
type : number
}
}
[1] : {
key : {
description : 2
overflow : false
properties : [
]
type : number
}
value : {
description : 2
overflow : false
properties : [
]
type : number
}
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ InspectorTest.runTestSuite([
function iteratorObject(next)
{
checkExpression("(new Map([[1,2]])).entries()")
.then(() => checkExpression("(new Set([[1,2]])).entries()"))
.then(() => checkExpression("(new Set([1,2])).entries()"))
.then(next);
},

Expand Down
Loading

0 comments on commit 74571c8

Please sign in to comment.