diff --git a/include/v8-util.h b/include/v8-util.h index 1ee1c72134..21990a8fd9 100644 --- a/include/v8-util.h +++ b/include/v8-util.h @@ -134,6 +134,9 @@ class DefaultGlobalMapTraits : public StdMapTraits { } static void DisposeCallbackData(WeakCallbackInfoType* data) {} static void Dispose(Isolate* isolate, Global value, K key) {} + static void DisposeWeak(Isolate* isolate, + const WeakCallbackInfo& data, + K key) {} private: template @@ -319,6 +322,8 @@ class PersistentValueMapBase { return p.Pass(); } + void RemoveWeak(const K& key) { Traits::Remove(&impl_, key); } + private: PersistentValueMapBase(PersistentValueMapBase&); void operator=(PersistentValueMapBase&); @@ -469,8 +474,8 @@ class GlobalValueMap : public PersistentValueMapBase { GlobalValueMap* persistentValueMap = Traits::MapFromWeakCallbackInfo(data); K key = Traits::KeyFromWeakCallbackInfo(data); - Traits::Dispose(data.GetIsolate(), persistentValueMap->Remove(key).Pass(), - key); + persistentValueMap->RemoveWeak(key); + Traits::DisposeWeak(data.GetIsolate(), data, key); Traits::DisposeCallbackData(data.GetParameter()); } } diff --git a/include/v8.h b/include/v8.h index a44b094f4e..6ce2df1160 100644 --- a/include/v8.h +++ b/include/v8.h @@ -471,6 +471,9 @@ template class Eternal { }; +static const int kInternalFieldsInWeakCallback = 2; + + template class WeakCallbackInfo { public: @@ -478,21 +481,28 @@ class WeakCallbackInfo { WeakCallbackInfo(Isolate* isolate, T* parameter, void* internal_field1, void* internal_field2) - : isolate_(isolate), - parameter_(parameter), - internal_field1_(internal_field1), - internal_field2_(internal_field2) {} + : isolate_(isolate), parameter_(parameter) { + internal_fields_[0] = internal_field1; + internal_fields_[1] = internal_field2; + } V8_INLINE Isolate* GetIsolate() const { return isolate_; } V8_INLINE T* GetParameter() const { return parameter_; } - V8_INLINE void* GetInternalField1() const { return internal_field1_; } - V8_INLINE void* GetInternalField2() const { return internal_field2_; } + V8_INLINE void* GetInternalField(int index) const; + + V8_INLINE V8_DEPRECATE_SOON("use indexed version", + void* GetInternalField1()) const { + return internal_fields_[0]; + } + V8_INLINE V8_DEPRECATE_SOON("use indexed version", + void* GetInternalField2()) const { + return internal_fields_[1]; + } private: Isolate* isolate_; T* parameter_; - void* internal_field1_; - void* internal_field2_; + void* internal_fields_[2]; }; @@ -5976,6 +5986,7 @@ class V8_EXPORT V8 { static void CheckIsJust(bool is_just); static void ToLocalEmpty(); + static void InternalFieldOutOfBounds(int index); template friend class Handle; template friend class Local; @@ -5983,6 +5994,8 @@ class V8_EXPORT V8 { friend class MaybeLocal; template friend class Maybe; + template + friend class WeakCallbackInfo; template friend class Eternal; template friend class PersistentBase; template friend class Persistent; @@ -6834,6 +6847,17 @@ Local MaybeLocal::ToLocalChecked() { } +template +void* WeakCallbackInfo::GetInternalField(int index) const { +#ifdef V8_ENABLE_CHECKS + if (index < 0 || index >= kInternalFieldsInWeakCallback) { + V8::InternalFieldOutOfBounds(index); + } +#endif + return internal_fields_[index]; +} + + template T* PersistentBase::New(Isolate* isolate, T* that) { if (that == NULL) return NULL; diff --git a/src/api.cc b/src/api.cc index 2d2bd1f23b..78328b74fb 100644 --- a/src/api.cc +++ b/src/api.cc @@ -600,6 +600,13 @@ void V8::ToLocalEmpty() { } +void V8::InternalFieldOutOfBounds(int index) { + Utils::ApiCheck(0 <= index && index < kInternalFieldsInWeakCallback, + "WeakCallbackInfo::GetInternalField", + "Internal field out of bounds."); +} + + // --- H a n d l e s --- diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 9b8be0a7a1..3ea67d8b9c 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -3193,6 +3193,124 @@ TEST(PersistentValueMap) { } +namespace { + +void* IntKeyToVoidPointer(int key) { return reinterpret_cast(key << 1); } + + +Local NewObjectForIntKey( + v8::Isolate* isolate, const v8::Global& templ, + int key) { + auto local = Local::New(isolate, templ); + auto obj = local->NewInstance(); + obj->SetAlignedPointerInInternalField(0, IntKeyToVoidPointer(key)); + return obj; +} + + +template +class PhantomStdMapTraits : public v8::StdMapTraits { + public: + typedef typename v8::GlobalValueMap> MapType; + static const v8::PersistentContainerCallbackType kCallbackType = + v8::kWeakWithInternalFields; + struct WeakCallbackDataType { + MapType* map; + K key; + }; + static WeakCallbackDataType* WeakCallbackParameter(MapType* map, const K& key, + Local value) { + WeakCallbackDataType* data = new WeakCallbackDataType; + data->map = map; + data->key = key; + return data; + } + static MapType* MapFromWeakCallbackInfo( + const v8::WeakCallbackInfo& data) { + return data.GetParameter()->map; + } + static K KeyFromWeakCallbackInfo( + const v8::WeakCallbackInfo& data) { + return data.GetParameter()->key; + } + static void DisposeCallbackData(WeakCallbackDataType* data) { delete data; } + static void Dispose(v8::Isolate* isolate, v8::Global value, K key) { + CHECK_EQ(IntKeyToVoidPointer(key), + v8::Object::GetAlignedPointerFromInternalField(value, 0)); + } + static void DisposeWeak( + v8::Isolate* isolate, + const v8::WeakCallbackInfo& info, K key) { + CHECK_EQ(IntKeyToVoidPointer(key), info.GetInternalField(0)); + } +}; +} + + +TEST(GlobalValueMap) { + typedef v8::GlobalValueMap> Map; + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::Global templ; + { + HandleScope scope(isolate); + auto t = ObjectTemplate::New(isolate); + t->SetInternalFieldCount(1); + templ.Reset(isolate, t); + } + Map map(isolate); + v8::internal::GlobalHandles* global_handles = + reinterpret_cast(isolate)->global_handles(); + int initial_handle_count = global_handles->global_handles_count(); + CHECK_EQ(0, static_cast(map.Size())); + { + HandleScope scope(isolate); + Local obj = map.Get(7); + CHECK(obj.IsEmpty()); + Local expected = v8::Object::New(isolate); + map.Set(7, expected); + CHECK_EQ(1, static_cast(map.Size())); + obj = map.Get(7); + CHECK(expected->Equals(obj)); + { + Map::PersistentValueReference ref = map.GetReference(7); + CHECK(expected->Equals(ref.NewLocal(isolate))); + } + v8::Global removed = map.Remove(7); + CHECK_EQ(0, static_cast(map.Size())); + CHECK(expected == removed); + removed = map.Remove(7); + CHECK(removed.IsEmpty()); + map.Set(8, expected); + CHECK_EQ(1, static_cast(map.Size())); + map.Set(8, expected); + CHECK_EQ(1, static_cast(map.Size())); + { + Map::PersistentValueReference ref; + Local expected2 = NewObjectForIntKey(isolate, templ, 8); + removed = map.Set(8, v8::Global(isolate, expected2), &ref); + CHECK_EQ(1, static_cast(map.Size())); + CHECK(expected == removed); + CHECK(expected2->Equals(ref.NewLocal(isolate))); + } + } + CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count()); + CcTest::i_isolate()->heap()->CollectAllGarbage( + i::Heap::kAbortIncrementalMarkingMask); + CHECK_EQ(0, static_cast(map.Size())); + CHECK_EQ(initial_handle_count, global_handles->global_handles_count()); + { + HandleScope scope(isolate); + Local value = NewObjectForIntKey(isolate, templ, 9); + map.Set(9, value); + map.Clear(); + } + CHECK_EQ(0, static_cast(map.Size())); + CHECK_EQ(initial_handle_count, global_handles->global_handles_count()); +} + + TEST(PersistentValueVector) { LocalContext env; v8::Isolate* isolate = env->GetIsolate();