Commit df472240 authored by adamk's avatar adamk Committed by Commit bot

Expose Map/Set methods through the API

Map: get, set, has, delete, clear
Set: add, has, delete, clear

All except clear are implemented as calls into collection.js.

Note that some of these shadow methods of v8::Object. It's unclear
how confusing that's going to be: on the one hand, it seems likely
that most operations you would want to do on a Map or Set are these.
On the other, generic code could get confused if it somehow gets
ahold of a variable that happens to be C++-typed as a v8::Map or v8::Set.

BUG=v8:3340
LOG=y

Review URL: https://codereview.chromium.org/1204623002

Cr-Commit-Position: refs/heads/master@{#29237}
parent bcb276c6
...@@ -2982,6 +2982,16 @@ class V8_EXPORT Array : public Object { ...@@ -2982,6 +2982,16 @@ class V8_EXPORT Array : public Object {
class V8_EXPORT Map : public Object { class V8_EXPORT Map : public Object {
public: public:
size_t Size() const; size_t Size() const;
void Clear();
V8_WARN_UNUSED_RESULT MaybeLocal<Value> Get(Local<Context> context,
Local<Value> key);
V8_WARN_UNUSED_RESULT MaybeLocal<Map> Set(Local<Context> context,
Local<Value> key,
Local<Value> value);
V8_WARN_UNUSED_RESULT Maybe<bool> Has(Local<Context> context,
Local<Value> key);
V8_WARN_UNUSED_RESULT Maybe<bool> Delete(Local<Context> context,
Local<Value> key);
/** /**
* Returns an array of length Size() * 2, where index N is the Nth key and * Returns an array of length Size() * 2, where index N is the Nth key and
...@@ -3016,6 +3026,13 @@ class V8_EXPORT Map : public Object { ...@@ -3016,6 +3026,13 @@ class V8_EXPORT Map : public Object {
class V8_EXPORT Set : public Object { class V8_EXPORT Set : public Object {
public: public:
size_t Size() const; size_t Size() const;
void Clear();
V8_WARN_UNUSED_RESULT MaybeLocal<Set> Add(Local<Context> context,
Local<Value> key);
V8_WARN_UNUSED_RESULT Maybe<bool> Has(Local<Context> context,
Local<Value> key);
V8_WARN_UNUSED_RESULT Maybe<bool> Delete(Local<Context> context,
Local<Value> key);
/** /**
* Returns an array of the keys in this Set. * Returns an array of the keys in this Set.
......
...@@ -6264,6 +6264,70 @@ size_t v8::Map::Size() const { ...@@ -6264,6 +6264,70 @@ size_t v8::Map::Size() const {
} }
void Map::Clear() {
auto self = Utils::OpenHandle(this);
i::Isolate* isolate = self->GetIsolate();
LOG_API(isolate, "Map::Clear");
ENTER_V8(isolate);
i::Runtime::JSMapClear(isolate, self);
}
MaybeLocal<Value> Map::Get(Local<Context> context, Local<Value> key) {
PREPARE_FOR_EXECUTION(context, "Map::Get", Value);
auto self = Utils::OpenHandle(this);
Local<Value> result;
i::Handle<i::Object> argv[] = {Utils::OpenHandle(*key)};
has_pending_exception =
!ToLocal<Value>(i::Execution::Call(isolate, isolate->map_get(), self,
arraysize(argv), argv, false),
&result);
RETURN_ON_FAILED_EXECUTION(Value);
RETURN_ESCAPED(result);
}
MaybeLocal<Map> Map::Set(Local<Context> context, Local<Value> key,
Local<Value> value) {
PREPARE_FOR_EXECUTION(context, "Map::Set", Map);
auto self = Utils::OpenHandle(this);
i::Handle<i::Object> result;
i::Handle<i::Object> argv[] = {Utils::OpenHandle(*key),
Utils::OpenHandle(*value)};
has_pending_exception =
!i::Execution::Call(isolate, isolate->map_set(), self, arraysize(argv),
argv, false).ToHandle(&result);
RETURN_ON_FAILED_EXECUTION(Map);
RETURN_ESCAPED(Local<Map>::Cast(Utils::ToLocal(result)));
}
Maybe<bool> Map::Has(Local<Context> context, Local<Value> key) {
PREPARE_FOR_EXECUTION_PRIMITIVE(context, "Map::Has", bool);
auto self = Utils::OpenHandle(this);
i::Handle<i::Object> result;
i::Handle<i::Object> argv[] = {Utils::OpenHandle(*key)};
has_pending_exception =
!i::Execution::Call(isolate, isolate->map_has(), self, arraysize(argv),
argv, false).ToHandle(&result);
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return Just(result->IsTrue());
}
Maybe<bool> Map::Delete(Local<Context> context, Local<Value> key) {
PREPARE_FOR_EXECUTION_PRIMITIVE(context, "Map::Delete", bool);
auto self = Utils::OpenHandle(this);
i::Handle<i::Object> result;
i::Handle<i::Object> argv[] = {Utils::OpenHandle(*key)};
has_pending_exception =
!i::Execution::Call(isolate, isolate->map_delete(), self, arraysize(argv),
argv, false).ToHandle(&result);
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return Just(result->IsTrue());
}
Local<Array> Map::AsArray() const { Local<Array> Map::AsArray() const {
i::Handle<i::JSMap> obj = Utils::OpenHandle(this); i::Handle<i::JSMap> obj = Utils::OpenHandle(this);
i::Isolate* isolate = obj->GetIsolate(); i::Isolate* isolate = obj->GetIsolate();
...@@ -6316,6 +6380,54 @@ size_t v8::Set::Size() const { ...@@ -6316,6 +6380,54 @@ size_t v8::Set::Size() const {
} }
void Set::Clear() {
auto self = Utils::OpenHandle(this);
i::Isolate* isolate = self->GetIsolate();
LOG_API(isolate, "Set::Clear");
ENTER_V8(isolate);
i::Runtime::JSSetClear(isolate, self);
}
MaybeLocal<Set> Set::Add(Local<Context> context, Local<Value> key) {
PREPARE_FOR_EXECUTION(context, "Set::Add", Set);
auto self = Utils::OpenHandle(this);
i::Handle<i::Object> result;
i::Handle<i::Object> argv[] = {Utils::OpenHandle(*key)};
has_pending_exception =
!i::Execution::Call(isolate, isolate->set_add(), self, arraysize(argv),
argv, false).ToHandle(&result);
RETURN_ON_FAILED_EXECUTION(Set);
RETURN_ESCAPED(Local<Set>::Cast(Utils::ToLocal(result)));
}
Maybe<bool> Set::Has(Local<Context> context, Local<Value> key) {
PREPARE_FOR_EXECUTION_PRIMITIVE(context, "Set::Has", bool);
auto self = Utils::OpenHandle(this);
i::Handle<i::Object> result;
i::Handle<i::Object> argv[] = {Utils::OpenHandle(*key)};
has_pending_exception =
!i::Execution::Call(isolate, isolate->set_has(), self, arraysize(argv),
argv, false).ToHandle(&result);
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return Just(result->IsTrue());
}
Maybe<bool> Set::Delete(Local<Context> context, Local<Value> key) {
PREPARE_FOR_EXECUTION_PRIMITIVE(context, "Set::Delete", bool);
auto self = Utils::OpenHandle(this);
i::Handle<i::Object> result;
i::Handle<i::Object> argv[] = {Utils::OpenHandle(*key)};
has_pending_exception =
!i::Execution::Call(isolate, isolate->set_delete(), self, arraysize(argv),
argv, false).ToHandle(&result);
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return Just(result->IsTrue());
}
Local<Array> Set::AsArray() const { Local<Array> Set::AsArray() const {
i::Handle<i::JSSet> obj = Utils::OpenHandle(this); i::Handle<i::JSSet> obj = Utils::OpenHandle(this);
i::Isolate* isolate = obj->GetIsolate(); i::Isolate* isolate = obj->GetIsolate();
......
...@@ -1711,6 +1711,13 @@ void Genesis::InstallNativeFunctions() { ...@@ -1711,6 +1711,13 @@ void Genesis::InstallNativeFunctions() {
INSTALL_NATIVE(JSFunction, "$observeNativeObjectNotifierPerformChange", INSTALL_NATIVE(JSFunction, "$observeNativeObjectNotifierPerformChange",
native_object_notifier_perform_change); native_object_notifier_perform_change);
INSTALL_NATIVE(JSFunction, "$arrayValues", array_values_iterator); INSTALL_NATIVE(JSFunction, "$arrayValues", array_values_iterator);
INSTALL_NATIVE(JSFunction, "$mapGet", map_get);
INSTALL_NATIVE(JSFunction, "$mapSet", map_set);
INSTALL_NATIVE(JSFunction, "$mapHas", map_has);
INSTALL_NATIVE(JSFunction, "$mapDelete", map_delete);
INSTALL_NATIVE(JSFunction, "$setAdd", set_add);
INSTALL_NATIVE(JSFunction, "$setHas", set_has);
INSTALL_NATIVE(JSFunction, "$setDelete", set_delete);
INSTALL_NATIVE(JSFunction, "$mapFromArray", map_from_array); INSTALL_NATIVE(JSFunction, "$mapFromArray", map_from_array);
INSTALL_NATIVE(JSFunction, "$setFromArray", set_from_array); INSTALL_NATIVE(JSFunction, "$setFromArray", set_from_array);
} }
......
...@@ -4,6 +4,12 @@ ...@@ -4,6 +4,12 @@
var $getHash; var $getHash;
var $getExistingHash; var $getExistingHash;
var $mapSet;
var $mapHas;
var $mapDelete;
var $setAdd;
var $setHas;
var $setDelete;
var $mapFromArray; var $mapFromArray;
var $setFromArray; var $setFromArray;
...@@ -481,6 +487,13 @@ utils.InstallFunctions(GlobalMap.prototype, DONT_ENUM, [ ...@@ -481,6 +487,13 @@ utils.InstallFunctions(GlobalMap.prototype, DONT_ENUM, [
// Expose to the global scope. // Expose to the global scope.
$getHash = GetHash; $getHash = GetHash;
$getExistingHash = GetExistingHash; $getExistingHash = GetExistingHash;
$mapGet = MapGet;
$mapSet = MapSet;
$mapHas = MapHas;
$mapDelete = MapDelete;
$setAdd = SetAdd;
$setHas = SetHas;
$setDelete = SetDelete;
$mapFromArray = function(array) { $mapFromArray = function(array) {
var map = new GlobalMap; var map = new GlobalMap;
......
...@@ -191,6 +191,13 @@ enum BindingFlags { ...@@ -191,6 +191,13 @@ enum BindingFlags {
V(JS_MAP_MAP_INDEX, Map, js_map_map) \ V(JS_MAP_MAP_INDEX, Map, js_map_map) \
V(JS_SET_FUN_INDEX, JSFunction, js_set_fun) \ V(JS_SET_FUN_INDEX, JSFunction, js_set_fun) \
V(JS_SET_MAP_INDEX, Map, js_set_map) \ V(JS_SET_MAP_INDEX, Map, js_set_map) \
V(MAP_GET_METHOD_INDEX, JSFunction, map_get) \
V(MAP_SET_METHOD_INDEX, JSFunction, map_set) \
V(MAP_HAS_METHOD_INDEX, JSFunction, map_has) \
V(MAP_DELETE_METHOD_INDEX, JSFunction, map_delete) \
V(SET_ADD_METHOD_INDEX, JSFunction, set_add) \
V(SET_HAS_METHOD_INDEX, JSFunction, set_has) \
V(SET_DELETE_METHOD_INDEX, JSFunction, set_delete) \
V(MAP_FROM_ARRAY_INDEX, JSFunction, map_from_array) \ V(MAP_FROM_ARRAY_INDEX, JSFunction, map_from_array) \
V(SET_FROM_ARRAY_INDEX, JSFunction, set_from_array) \ V(SET_FROM_ARRAY_INDEX, JSFunction, set_from_array) \
V(MAP_ITERATOR_MAP_INDEX, Map, map_iterator_map) \ V(MAP_ITERATOR_MAP_INDEX, Map, map_iterator_map) \
...@@ -437,6 +444,13 @@ class Context: public FixedArray { ...@@ -437,6 +444,13 @@ class Context: public FixedArray {
JS_MAP_MAP_INDEX, JS_MAP_MAP_INDEX,
JS_SET_FUN_INDEX, JS_SET_FUN_INDEX,
JS_SET_MAP_INDEX, JS_SET_MAP_INDEX,
MAP_GET_METHOD_INDEX,
MAP_SET_METHOD_INDEX,
MAP_HAS_METHOD_INDEX,
MAP_DELETE_METHOD_INDEX,
SET_ADD_METHOD_INDEX,
SET_HAS_METHOD_INDEX,
SET_DELETE_METHOD_INDEX,
MAP_FROM_ARRAY_INDEX, MAP_FROM_ARRAY_INDEX,
SET_FROM_ARRAY_INDEX, SET_FROM_ARRAY_INDEX,
MAP_ITERATOR_MAP_INDEX, MAP_ITERATOR_MAP_INDEX,
......
...@@ -82,13 +82,18 @@ RUNTIME_FUNCTION(Runtime_SetShrink) { ...@@ -82,13 +82,18 @@ RUNTIME_FUNCTION(Runtime_SetShrink) {
} }
void Runtime::JSSetClear(Isolate* isolate, Handle<JSSet> set) {
Handle<OrderedHashSet> table(OrderedHashSet::cast(set->table()));
table = OrderedHashSet::Clear(table);
set->set_table(*table);
}
RUNTIME_FUNCTION(Runtime_SetClear) { RUNTIME_FUNCTION(Runtime_SetClear) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 1); DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0); CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table())); Runtime::JSSetClear(isolate, holder);
table = OrderedHashSet::Clear(table);
holder->set_table(*table);
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
...@@ -174,13 +179,18 @@ RUNTIME_FUNCTION(Runtime_MapShrink) { ...@@ -174,13 +179,18 @@ RUNTIME_FUNCTION(Runtime_MapShrink) {
} }
void Runtime::JSMapClear(Isolate* isolate, Handle<JSMap> map) {
Handle<OrderedHashMap> table(OrderedHashMap::cast(map->table()));
table = OrderedHashMap::Clear(table);
map->set_table(*table);
}
RUNTIME_FUNCTION(Runtime_MapClear) { RUNTIME_FUNCTION(Runtime_MapClear) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK(args.length() == 1); DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0); CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table())); Runtime::JSMapClear(isolate, holder);
table = OrderedHashMap::Clear(table);
holder->set_table(*table);
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
......
...@@ -872,7 +872,9 @@ class Runtime : public AllStatic { ...@@ -872,7 +872,9 @@ class Runtime : public AllStatic {
static void JSMapInitialize(Isolate* isolate, Handle<JSMap> map); static void JSMapInitialize(Isolate* isolate, Handle<JSMap> map);
static void JSMapClear(Isolate* isolate, Handle<JSMap> map);
static void JSSetInitialize(Isolate* isolate, Handle<JSSet> set); static void JSSetInitialize(Isolate* isolate, Handle<JSSet> set);
static void JSSetClear(Isolate* isolate, Handle<JSSet> set);
static void WeakCollectionInitialize( static void WeakCollectionInitialize(
Isolate* isolate, Handle<JSWeakCollection> weak_collection); Isolate* isolate, Handle<JSWeakCollection> weak_collection);
......
...@@ -21584,8 +21584,43 @@ TEST(Map) { ...@@ -21584,8 +21584,43 @@ TEST(Map) {
map = v8::Map::FromArray(env.local(), contents).ToLocalChecked(); map = v8::Map::FromArray(env.local(), contents).ToLocalChecked();
CHECK_EQ(2U, map->Size()); CHECK_EQ(2U, map->Size());
CHECK(map->Has(env.local(), v8::Integer::New(isolate, 1)).FromJust());
CHECK(map->Has(env.local(), v8::Integer::New(isolate, 3)).FromJust());
CHECK(!map->Has(env.local(), v8::Integer::New(isolate, 2)).FromJust());
CHECK(!map->Has(env.local(), map).FromJust());
CHECK_EQ(2, map->Get(env.local(), v8::Integer::New(isolate, 1))
.ToLocalChecked()
->Int32Value());
CHECK_EQ(4, map->Get(env.local(), v8::Integer::New(isolate, 3))
.ToLocalChecked()
->Int32Value());
CHECK(map->Get(env.local(), v8::Integer::New(isolate, 42))
.ToLocalChecked()
->IsUndefined());
CHECK(!map->Set(env.local(), map, map).IsEmpty());
CHECK_EQ(3U, map->Size());
CHECK(map->Has(env.local(), map).FromJust());
CHECK(map->Delete(env.local(), map).FromJust());
CHECK_EQ(2U, map->Size());
CHECK(!map->Has(env.local(), map).FromJust());
CHECK(!map->Delete(env.local(), map).FromJust());
map->Clear();
CHECK_EQ(0U, map->Size());
}
TEST(MapFromArrayOddLength) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope handle_scope(isolate);
LocalContext env;
// Odd lengths result in a null MaybeLocal. // Odd lengths result in a null MaybeLocal.
contents = v8::Array::New(isolate, 41); Local<v8::Array> contents = v8::Array::New(isolate, 41);
CHECK(v8::Map::FromArray(env.local(), contents).IsEmpty()); CHECK(v8::Map::FromArray(env.local(), contents).IsEmpty());
} }
...@@ -21613,4 +21648,22 @@ TEST(Set) { ...@@ -21613,4 +21648,22 @@ TEST(Set) {
set = v8::Set::FromArray(env.local(), keys).ToLocalChecked(); set = v8::Set::FromArray(env.local(), keys).ToLocalChecked();
CHECK_EQ(2U, set->Size()); CHECK_EQ(2U, set->Size());
CHECK(set->Has(env.local(), v8::Integer::New(isolate, 1)).FromJust());
CHECK(set->Has(env.local(), v8::Integer::New(isolate, 2)).FromJust());
CHECK(!set->Has(env.local(), v8::Integer::New(isolate, 3)).FromJust());
CHECK(!set->Has(env.local(), set).FromJust());
CHECK(!set->Add(env.local(), set).IsEmpty());
CHECK_EQ(3U, set->Size());
CHECK(set->Has(env.local(), set).FromJust());
CHECK(set->Delete(env.local(), set).FromJust());
CHECK_EQ(2U, set->Size());
CHECK(!set->Has(env.local(), set).FromJust());
CHECK(!set->Delete(env.local(), set).FromJust());
set->Clear();
CHECK_EQ(0U, set->Size());
} }
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