Commit 74571c80 authored by Ruben Bridgewater's avatar Ruben Bridgewater Committed by Commit Bot

Fix preview of set entries

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: https://github.com/nodejs/node/issues/24629

R=yangguo@chromium.org

Change-Id: I669a724bb4afaf5a713e468b1f51691d22c25253
Reviewed-on: https://chromium-review.googlesource.com/c/1350790
Commit-Queue: Yang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59311}
parent 58d5361d
...@@ -148,6 +148,7 @@ Rick Waldron <waldron.rick@gmail.com> ...@@ -148,6 +148,7 @@ Rick Waldron <waldron.rick@gmail.com>
Rob Wu <rob@robwu.nl> Rob Wu <rob@robwu.nl>
Robert Mustacchi <rm@fingolfin.org> Robert Mustacchi <rm@fingolfin.org>
Robert Nagy <robert.nagy@gmail.com> Robert Nagy <robert.nagy@gmail.com>
Ruben Bridgewater <ruben@bridgewater.de>
Ryan Dahl <ry@tinyclouds.org> Ryan Dahl <ry@tinyclouds.org>
Sakthipriyan Vairamani (thefourtheye) <thechargingvolcano@gmail.com> Sakthipriyan Vairamani (thefourtheye) <thechargingvolcano@gmail.com>
Sander Mathijs van Veen <sander@leaningtech.com> Sander Mathijs van Veen <sander@leaningtech.com>
......
...@@ -6985,6 +6985,11 @@ enum class MapAsArrayKind { ...@@ -6985,6 +6985,11 @@ enum class MapAsArrayKind {
kValues = i::JS_MAP_VALUE_ITERATOR_TYPE 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, i::Handle<i::JSArray> MapAsArray(i::Isolate* isolate, i::Object table_obj,
int offset, MapAsArrayKind kind) { int offset, MapAsArrayKind kind) {
i::Factory* factory = isolate->factory(); i::Factory* factory = isolate->factory();
...@@ -7094,13 +7099,14 @@ Maybe<bool> Set::Delete(Local<Context> context, Local<Value> key) { ...@@ -7094,13 +7099,14 @@ Maybe<bool> Set::Delete(Local<Context> context, Local<Value> key) {
namespace { namespace {
i::Handle<i::JSArray> SetAsArray(i::Isolate* isolate, i::Object table_obj, i::Handle<i::JSArray> SetAsArray(i::Isolate* isolate, i::Object table_obj,
int offset) { int offset, SetAsArrayKind kind) {
i::Factory* factory = isolate->factory(); i::Factory* factory = isolate->factory();
i::Handle<i::OrderedHashSet> table(i::OrderedHashSet::cast(table_obj), i::Handle<i::OrderedHashSet> table(i::OrderedHashSet::cast(table_obj),
isolate); isolate);
// Elements skipped by |offset| may already be deleted. // Elements skipped by |offset| may already be deleted.
int capacity = table->UsedCapacity(); 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); if (max_length == 0) return factory->NewJSArray(0);
i::Handle<i::FixedArray> result = factory->NewFixedArray(max_length); i::Handle<i::FixedArray> result = factory->NewFixedArray(max_length);
int result_index = 0; int result_index = 0;
...@@ -7111,6 +7117,7 @@ i::Handle<i::JSArray> SetAsArray(i::Isolate* isolate, i::Object table_obj, ...@@ -7111,6 +7117,7 @@ i::Handle<i::JSArray> SetAsArray(i::Isolate* isolate, i::Object table_obj,
i::Object key = table->KeyAt(i); i::Object key = table->KeyAt(i);
if (key == the_hole) continue; if (key == the_hole) continue;
result->set(result_index++, key); result->set(result_index++, key);
if (collect_key_values) result->set(result_index++, key);
} }
} }
DCHECK_GE(max_length, result_index); DCHECK_GE(max_length, result_index);
...@@ -7126,7 +7133,8 @@ Local<Array> Set::AsArray() const { ...@@ -7126,7 +7133,8 @@ Local<Array> Set::AsArray() const {
i::Isolate* isolate = obj->GetIsolate(); i::Isolate* isolate = obj->GetIsolate();
LOG_API(isolate, Set, AsArray); LOG_API(isolate, Set, AsArray);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate); 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));
} }
...@@ -9553,21 +9561,22 @@ v8::MaybeLocal<v8::Array> v8::Object::PreviewEntries(bool* is_key_value) { ...@@ -9553,21 +9561,22 @@ v8::MaybeLocal<v8::Array> v8::Object::PreviewEntries(bool* is_key_value) {
i::Handle<i::JSWeakCollection>::cast(object), 0)); i::Handle<i::JSWeakCollection>::cast(object), 0));
} }
if (object->IsJSMapIterator()) { if (object->IsJSMapIterator()) {
i::Handle<i::JSMapIterator> iterator = i::Handle<i::JSMapIterator> it = i::Handle<i::JSMapIterator>::cast(object);
i::Handle<i::JSMapIterator>::cast(object);
MapAsArrayKind const kind = MapAsArrayKind const kind =
static_cast<MapAsArrayKind>(iterator->map()->instance_type()); static_cast<MapAsArrayKind>(it->map()->instance_type());
*is_key_value = kind == MapAsArrayKind::kEntries; *is_key_value = kind == MapAsArrayKind::kEntries;
if (!iterator->HasMore()) return v8::Array::New(v8_isolate); if (!it->HasMore()) return v8::Array::New(v8_isolate);
return Utils::ToLocal(MapAsArray(isolate, iterator->table(), return Utils::ToLocal(
i::Smi::ToInt(iterator->index()), kind)); MapAsArray(isolate, it->table(), i::Smi::ToInt(it->index()), kind));
} }
if (object->IsJSSetIterator()) { if (object->IsJSSetIterator()) {
i::Handle<i::JSSetIterator> it = i::Handle<i::JSSetIterator>::cast(object); 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); if (!it->HasMore()) return v8::Array::New(v8_isolate);
return Utils::ToLocal( 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>(); return v8::MaybeLocal<v8::Array>();
} }
......
...@@ -28649,7 +28649,7 @@ TEST(MicrotaskContextShouldBeNativeContext) { ...@@ -28649,7 +28649,7 @@ TEST(MicrotaskContextShouldBeNativeContext) {
isolate->RunMicrotasks(); isolate->RunMicrotasks();
} }
TEST(PreviewSetIteratorEntriesWithDeleted) { TEST(PreviewSetKeysIteratorEntriesWithDeleted) {
LocalContext env; LocalContext env;
v8::HandleScope handle_scope(env->GetIsolate()); v8::HandleScope handle_scope(env->GetIsolate());
v8::Local<v8::Context> context = env.local(); v8::Local<v8::Context> context = env.local();
...@@ -28748,7 +28748,142 @@ TEST(PreviewSetIteratorEntriesWithDeleted) { ...@@ -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; LocalContext env;
v8::HandleScope handle_scope(env->GetIsolate()); v8::HandleScope handle_scope(env->GetIsolate());
v8::Local<v8::Context> context = env.local(); v8::Local<v8::Context> context = env.local();
...@@ -28863,6 +28998,97 @@ TEST(PreviewMapIteratorEntriesWithDeleted) { ...@@ -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 { namespace {
static v8::Isolate* isolate_1; static v8::Isolate* isolate_1;
static v8::Isolate* isolate_2; static v8::Isolate* isolate_2;
...@@ -166,27 +166,39 @@ expression: (new Map([[1,2]])).entries() ...@@ -166,27 +166,39 @@ expression: (new Map([[1,2]])).entries()
} }
] ]
expression: (new Set([[1,2]])).entries() expression: (new Set([1,2])).entries()
[[Entries]]: [[Entries]]:
[ [
[0] : { [0] : {
key : {
description : 1
overflow : false
properties : [
]
type : number
}
value : { value : {
description : Array(2) description : 1
overflow : false overflow : false
properties : [ properties : [
[0] : {
name : 0
type : number
value : 1
}
[1] : {
name : 1
type : number
value : 2
}
] ]
subtype : array type : number
type : object }
}
[1] : {
key : {
description : 2
overflow : false
properties : [
]
type : number
}
value : {
description : 2
overflow : false
properties : [
]
type : number
} }
} }
] ]
......
...@@ -46,7 +46,7 @@ InspectorTest.runTestSuite([ ...@@ -46,7 +46,7 @@ InspectorTest.runTestSuite([
function iteratorObject(next) function iteratorObject(next)
{ {
checkExpression("(new Map([[1,2]])).entries()") checkExpression("(new Map([[1,2]])).entries()")
.then(() => checkExpression("(new Set([[1,2]])).entries()")) .then(() => checkExpression("(new Set([1,2])).entries()"))
.then(next); .then(next);
}, },
......
...@@ -596,6 +596,7 @@ expression: it = new Set([1,2]).keys(); it.next(); it ...@@ -596,6 +596,7 @@ expression: it = new Set([1,2]).keys(); it.next(); it
expression: it = new Set([1,2]).entries(); it.next(); it expression: it = new Set([1,2]).entries(); it.next(); it
[ [
[0] : { [0] : {
key : 2
value : 2 value : 2
} }
] ]
...@@ -610,7 +611,7 @@ expression: it = new Set([1,2]).entries(); it.next(); it ...@@ -610,7 +611,7 @@ expression: it = new Set([1,2]).entries(); it.next(); it
name : 0 name : 0
value : { value : {
className : Object className : Object
description : 2 description : {2 => 2}
objectId : <objectId> objectId : <objectId>
subtype : internal#entry subtype : internal#entry
type : object type : object
......
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