Expose Map/Set methods through the API
authoradamk <adamk@chromium.org>
Tue, 23 Jun 2015 15:14:06 +0000 (08:14 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 23 Jun 2015 15:14:17 +0000 (15:14 +0000)
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}

include/v8.h
src/api.cc
src/bootstrapper.cc
src/collection.js
src/contexts.h
src/runtime/runtime-collections.cc
src/runtime/runtime.h
test/cctest/test-api.cc

index 0caec4317268448d4f87ba85537dd78b5ad29ade..e16c2b372f1f0eb4eaf471ad7cc06b45b6738d2b 100644 (file)
@@ -2982,6 +2982,16 @@ class V8_EXPORT Array : public Object {
 class V8_EXPORT Map : public Object {
  public:
   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
@@ -3016,6 +3026,13 @@ class V8_EXPORT Map : public Object {
 class V8_EXPORT Set : public Object {
  public:
   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.
index ea1da695851a8d6a2fb313fe80f8eb8bddeeeb5a..3b2c9909bb8e537c09fed4fdd8cb26881dea3b58 100644 (file)
@@ -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 {
   i::Handle<i::JSMap> obj = Utils::OpenHandle(this);
   i::Isolate* isolate = obj->GetIsolate();
@@ -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 {
   i::Handle<i::JSSet> obj = Utils::OpenHandle(this);
   i::Isolate* isolate = obj->GetIsolate();
index c38e9da4fb55450679d6351db91c1cb0827b05a4..b0a625775a9576d3808c538601f36463ef6c807a 100644 (file)
@@ -1711,6 +1711,13 @@ void Genesis::InstallNativeFunctions() {
   INSTALL_NATIVE(JSFunction, "$observeNativeObjectNotifierPerformChange",
                  native_object_notifier_perform_change);
   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, "$setFromArray", set_from_array);
 }
index 86898529c3b7e8289e55b33696307c216eb3dfe5..ceab1642c557bf2526a9f3e557afb5d5299061c8 100644 (file)
@@ -4,6 +4,12 @@
 
 var $getHash;
 var $getExistingHash;
+var $mapSet;
+var $mapHas;
+var $mapDelete;
+var $setAdd;
+var $setHas;
+var $setDelete;
 var $mapFromArray;
 var $setFromArray;
 
@@ -481,6 +487,13 @@ utils.InstallFunctions(GlobalMap.prototype, DONT_ENUM, [
 // Expose to the global scope.
 $getHash = GetHash;
 $getExistingHash = GetExistingHash;
+$mapGet = MapGet;
+$mapSet = MapSet;
+$mapHas = MapHas;
+$mapDelete = MapDelete;
+$setAdd = SetAdd;
+$setHas = SetHas;
+$setDelete = SetDelete;
 
 $mapFromArray = function(array) {
   var map = new GlobalMap;
index 670f5a7c0ef693329eb1116a4df742965b39b145..78d4bc4255c5194d374fcf1c38cca902978b8a14 100644 (file)
@@ -191,6 +191,13 @@ enum BindingFlags {
   V(JS_MAP_MAP_INDEX, Map, js_map_map)                                         \
   V(JS_SET_FUN_INDEX, JSFunction, js_set_fun)                                  \
   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(SET_FROM_ARRAY_INDEX, JSFunction, set_from_array)                          \
   V(MAP_ITERATOR_MAP_INDEX, Map, map_iterator_map)                             \
@@ -437,6 +444,13 @@ class Context: public FixedArray {
     JS_MAP_MAP_INDEX,
     JS_SET_FUN_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,
     SET_FROM_ARRAY_INDEX,
     MAP_ITERATOR_MAP_INDEX,
index 94113fd69fb5457441b611e3410d27b156463c37..1ba1e34356caf5d93e425c7f3b0e6c337b655107 100644 (file)
@@ -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) {
   HandleScope scope(isolate);
   DCHECK(args.length() == 1);
   CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
-  Handle<OrderedHashSet> table(OrderedHashSet::cast(holder->table()));
-  table = OrderedHashSet::Clear(table);
-  holder->set_table(*table);
+  Runtime::JSSetClear(isolate, holder);
   return isolate->heap()->undefined_value();
 }
 
@@ -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) {
   HandleScope scope(isolate);
   DCHECK(args.length() == 1);
   CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
-  Handle<OrderedHashMap> table(OrderedHashMap::cast(holder->table()));
-  table = OrderedHashMap::Clear(table);
-  holder->set_table(*table);
+  Runtime::JSMapClear(isolate, holder);
   return isolate->heap()->undefined_value();
 }
 
index 96d8a741e67f9135d7f11ec9158d03fa56092a85..c9db4e74cf3ebed728a4c0873462c22f5c09829e 100644 (file)
@@ -872,7 +872,9 @@ class Runtime : public AllStatic {
 
 
   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 JSSetClear(Isolate* isolate, Handle<JSSet> set);
 
   static void WeakCollectionInitialize(
       Isolate* isolate, Handle<JSWeakCollection> weak_collection);
index 3d8af9f3f21261040178df44d484b4f8e44c13e3..d0e7a87ea0c04a70b64a07c798a5c7f43edf225c 100644 (file)
@@ -21584,8 +21584,43 @@ TEST(Map) {
   map = v8::Map::FromArray(env.local(), contents).ToLocalChecked();
   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.
-  contents = v8::Array::New(isolate, 41);
+  Local<v8::Array> contents = v8::Array::New(isolate, 41);
   CHECK(v8::Map::FromArray(env.local(), contents).IsEmpty());
 }
 
@@ -21613,4 +21648,22 @@ TEST(Set) {
 
   set = v8::Set::FromArray(env.local(), keys).ToLocalChecked();
   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());
 }