Commit a5593679 authored by yurys's avatar yurys Committed by Commit bot

Add NativeWeakMap to v8.h

A new map wich references its keys weakly is added to v8.h. Internally it uses the same storage as JSWeakMap but doesn't depend on the JavaScript part of WeakMap implementation in weak-collection.js, hence it can be instantiated without entering any context.

BUG=chromium:437416
LOG=Y

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

Cr-Commit-Position: refs/heads/master@{#26451}
parent b7d27ea5
......@@ -1536,6 +1536,21 @@ class V8_EXPORT JSON {
};
/**
* A map whose keys are referenced weakly. It is similar to JavaScript WeakMap
* but can be created without entering a v8::Context and hence shouldn't
* escape to JavaScript.
*/
class V8_EXPORT NativeWeakMap : public Data {
public:
static Local<NativeWeakMap> New(Isolate* isolate);
void Set(Handle<Value> key, Handle<Value> value);
Local<Value> Get(Handle<Value> key);
bool Has(Handle<Value> key);
bool Delete(Handle<Value> key);
};
// --- Value ---
......
......@@ -2250,6 +2250,101 @@ bool StackFrame::IsConstructor() const {
}
// --- N a t i v e W e a k M a p ---
Local<NativeWeakMap> NativeWeakMap::New(Isolate* v8_isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8(isolate);
i::Handle<i::JSWeakMap> weakmap = isolate->factory()->NewJSWeakMap();
i::Runtime::WeakCollectionInitialize(isolate, weakmap);
return Utils::NativeWeakMapToLocal(weakmap);
}
void NativeWeakMap::Set(Handle<Value> v8_key, Handle<Value> v8_value) {
i::Handle<i::JSWeakMap> weak_collection = Utils::OpenHandle(this);
i::Isolate* isolate = weak_collection->GetIsolate();
ENTER_V8(isolate);
i::HandleScope scope(isolate);
i::Handle<i::Object> key = Utils::OpenHandle(*v8_key);
i::Handle<i::Object> value = Utils::OpenHandle(*v8_value);
if (!key->IsJSReceiver() && !key->IsSymbol()) {
DCHECK(false);
return;
}
i::Handle<i::ObjectHashTable> table(
i::ObjectHashTable::cast(weak_collection->table()));
if (!table->IsKey(*key)) {
DCHECK(false);
return;
}
i::Runtime::WeakCollectionSet(weak_collection, key, value);
}
Local<Value> NativeWeakMap::Get(Handle<Value> v8_key) {
i::Handle<i::JSWeakMap> weak_collection = Utils::OpenHandle(this);
i::Isolate* isolate = weak_collection->GetIsolate();
ENTER_V8(isolate);
i::Handle<i::Object> key = Utils::OpenHandle(*v8_key);
if (!key->IsJSReceiver() && !key->IsSymbol()) {
DCHECK(false);
return v8::Undefined(reinterpret_cast<v8::Isolate*>(isolate));
}
i::Handle<i::ObjectHashTable> table(
i::ObjectHashTable::cast(weak_collection->table()));
if (!table->IsKey(*key)) {
DCHECK(false);
return v8::Undefined(reinterpret_cast<v8::Isolate*>(isolate));
}
i::Handle<i::Object> lookup(table->Lookup(key), isolate);
if (lookup->IsTheHole())
return v8::Undefined(reinterpret_cast<v8::Isolate*>(isolate));
return Utils::ToLocal(lookup);
}
bool NativeWeakMap::Has(Handle<Value> v8_key) {
i::Handle<i::JSWeakMap> weak_collection = Utils::OpenHandle(this);
i::Isolate* isolate = weak_collection->GetIsolate();
ENTER_V8(isolate);
i::HandleScope scope(isolate);
i::Handle<i::Object> key = Utils::OpenHandle(*v8_key);
if (!key->IsJSReceiver() && !key->IsSymbol()) {
DCHECK(false);
return false;
}
i::Handle<i::ObjectHashTable> table(
i::ObjectHashTable::cast(weak_collection->table()));
if (!table->IsKey(*key)) {
DCHECK(false);
return false;
}
i::Handle<i::Object> lookup(table->Lookup(key), isolate);
return !lookup->IsTheHole();
}
bool NativeWeakMap::Delete(Handle<Value> v8_key) {
i::Handle<i::JSWeakMap> weak_collection = Utils::OpenHandle(this);
i::Isolate* isolate = weak_collection->GetIsolate();
ENTER_V8(isolate);
i::HandleScope scope(isolate);
i::Handle<i::Object> key = Utils::OpenHandle(*v8_key);
if (!key->IsJSReceiver() && !key->IsSymbol()) {
DCHECK(false);
return false;
}
i::Handle<i::ObjectHashTable> table(
i::ObjectHashTable::cast(weak_collection->table()));
if (!table->IsKey(*key)) {
DCHECK(false);
return false;
}
return i::Runtime::WeakCollectionDelete(weak_collection, key);
}
// --- J S O N ---
Local<Value> JSON::Parse(Local<String> json_string) {
......
......@@ -169,7 +169,8 @@ class RegisteredExtension {
V(Context, Context) \
V(External, Object) \
V(StackTrace, JSArray) \
V(StackFrame, JSObject)
V(StackFrame, JSObject) \
V(NativeWeakMap, JSWeakMap)
class Utils {
public:
......@@ -255,6 +256,8 @@ class Utils {
v8::internal::Handle<v8::internal::TypeSwitchInfo> obj);
static inline Local<External> ExternalToLocal(
v8::internal::Handle<v8::internal::JSObject> obj);
static inline Local<NativeWeakMap> NativeWeakMapToLocal(
v8::internal::Handle<v8::internal::JSWeakMap> obj);
#define DECLARE_OPEN_HANDLE(From, To) \
static inline v8::internal::Handle<v8::internal::To> \
......@@ -361,6 +364,7 @@ MAKE_TO_LOCAL(NumberToLocal, Object, Number)
MAKE_TO_LOCAL(IntegerToLocal, Object, Integer)
MAKE_TO_LOCAL(Uint32ToLocal, Object, Uint32)
MAKE_TO_LOCAL(ExternalToLocal, JSObject, External)
MAKE_TO_LOCAL(NativeWeakMapToLocal, JSWeakMap, NativeWeakMap)
#undef MAKE_TO_LOCAL_TYPED_ARRAY
#undef MAKE_TO_LOCAL
......
......@@ -2237,6 +2237,15 @@ Handle<JSObject> Factory::NewArgumentsObject(Handle<JSFunction> callee,
}
Handle<JSWeakMap> Factory::NewJSWeakMap() {
// TODO(adamk): Currently the map is only created three times per
// isolate. If it's created more often, the map should be moved into the
// strong root list.
Handle<Map> map = NewMap(JS_WEAK_MAP_TYPE, JSWeakMap::kSize);
return Handle<JSWeakMap>::cast(NewJSObjectFromMap(map));
}
Handle<Map> Factory::ObjectLiteralMapFromCache(Handle<Context> context,
int number_of_properties,
bool* is_result_from_cache) {
......
......@@ -359,6 +359,8 @@ class Factory FINAL {
return NewJSObjectFromMap(neander_map());
}
Handle<JSWeakMap> NewJSWeakMap();
Handle<JSObject> NewArgumentsObject(Handle<JSFunction> callee, int length);
// JS objects are pretenured when allocated by the bootstrapper and
......
......@@ -296,12 +296,11 @@ RUNTIME_FUNCTION(Runtime_MapIteratorNext) {
}
static Handle<JSWeakCollection> WeakCollectionInitialize(
void Runtime::WeakCollectionInitialize(
Isolate* isolate, Handle<JSWeakCollection> weak_collection) {
DCHECK(weak_collection->map()->inobject_properties() == 0);
Handle<ObjectHashTable> table = ObjectHashTable::New(isolate, 0);
weak_collection->set_table(*table);
return weak_collection;
}
......@@ -309,7 +308,8 @@ RUNTIME_FUNCTION(Runtime_WeakCollectionInitialize) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
return *WeakCollectionInitialize(isolate, weak_collection);
Runtime::WeakCollectionInitialize(isolate, weak_collection);
return *weak_collection;
}
......@@ -341,6 +341,24 @@ RUNTIME_FUNCTION(Runtime_WeakCollectionHas) {
}
bool Runtime::WeakCollectionDelete(Handle<JSWeakCollection> weak_collection,
Handle<Object> key) {
DCHECK(key->IsJSReceiver() || key->IsSymbol());
Handle<ObjectHashTable> table(
ObjectHashTable::cast(weak_collection->table()));
DCHECK(table->IsKey(*key));
bool was_present = false;
Handle<ObjectHashTable> new_table =
ObjectHashTable::Remove(table, key, &was_present);
weak_collection->set_table(*new_table);
if (*table != *new_table) {
// Zap the old table since we didn't record slots for its elements.
table->FillWithHoles(0, table->length());
}
return was_present;
}
RUNTIME_FUNCTION(Runtime_WeakCollectionDelete) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
......@@ -350,15 +368,23 @@ RUNTIME_FUNCTION(Runtime_WeakCollectionDelete) {
Handle<ObjectHashTable> table(
ObjectHashTable::cast(weak_collection->table()));
RUNTIME_ASSERT(table->IsKey(*key));
bool was_present = false;
Handle<ObjectHashTable> new_table =
ObjectHashTable::Remove(table, key, &was_present);
bool was_present = Runtime::WeakCollectionDelete(weak_collection, key);
return isolate->heap()->ToBoolean(was_present);
}
void Runtime::WeakCollectionSet(Handle<JSWeakCollection> weak_collection,
Handle<Object> key, Handle<Object> value) {
DCHECK(key->IsJSReceiver() || key->IsSymbol());
Handle<ObjectHashTable> table(
ObjectHashTable::cast(weak_collection->table()));
DCHECK(table->IsKey(*key));
Handle<ObjectHashTable> new_table = ObjectHashTable::Put(table, key, value);
weak_collection->set_table(*new_table);
if (*table != *new_table) {
// Zap the old table since we didn't record slots for its elements.
table->FillWithHoles(0, table->length());
}
return isolate->heap()->ToBoolean(was_present);
}
......@@ -372,12 +398,7 @@ RUNTIME_FUNCTION(Runtime_WeakCollectionSet) {
Handle<ObjectHashTable> table(
ObjectHashTable::cast(weak_collection->table()));
RUNTIME_ASSERT(table->IsKey(*key));
Handle<ObjectHashTable> new_table = ObjectHashTable::Put(table, key, value);
weak_collection->set_table(*new_table);
if (*table != *new_table) {
// Zap the old table since we didn't record slots for its elements.
table->FillWithHoles(0, table->length());
}
Runtime::WeakCollectionSet(weak_collection, key, value);
return *weak_collection;
}
......@@ -414,14 +435,9 @@ RUNTIME_FUNCTION(Runtime_GetWeakSetValues) {
RUNTIME_FUNCTION(Runtime_ObservationWeakMapCreate) {
HandleScope scope(isolate);
DCHECK(args.length() == 0);
// TODO(adamk): Currently this runtime function is only called three times per
// isolate. If it's called more often, the map should be moved into the
// strong root list.
Handle<Map> map =
isolate->factory()->NewMap(JS_WEAK_MAP_TYPE, JSWeakMap::kSize);
Handle<JSWeakMap> weakmap =
Handle<JSWeakMap>::cast(isolate->factory()->NewJSObjectFromMap(map));
return *WeakCollectionInitialize(isolate, weakmap);
Handle<JSWeakMap> weakmap = isolate->factory()->NewJSWeakMap();
Runtime::WeakCollectionInitialize(isolate, weakmap);
return *weakmap;
}
}
} // namespace v8::internal
......@@ -874,6 +874,13 @@ class Runtime : public AllStatic {
MUST_USE_RESULT static MaybeHandle<Object> CreateArrayLiteralBoilerplate(
Isolate* isolate, Handle<FixedArray> literals,
Handle<FixedArray> elements);
static void WeakCollectionInitialize(
Isolate* isolate, Handle<JSWeakCollection> weak_collection);
static void WeakCollectionSet(Handle<JSWeakCollection> weak_collection,
Handle<Object> key, Handle<Object> value);
static bool WeakCollectionDelete(Handle<JSWeakCollection> weak_collection,
Handle<Object> key);
};
......
......@@ -46,6 +46,7 @@
#include "src/isolate.h"
#include "src/objects.h"
#include "src/parser.h"
#include "src/smart-pointers.h"
#include "src/snapshot.h"
#include "src/unicode-inl.h"
#include "src/utils.h"
......@@ -4737,6 +4738,77 @@ TEST(MessageHandler5) {
}
TEST(NativeWeakMap) {
v8::Isolate* isolate = CcTest::isolate();
HandleScope scope(isolate);
Local<v8::NativeWeakMap> weak_map(v8::NativeWeakMap::New(isolate));
CHECK(!weak_map.IsEmpty());
LocalContext env;
Local<Object> value = v8::Object::New(isolate);
Local<Object> local1 = v8::Object::New(isolate);
CHECK(!weak_map->Has(local1));
CHECK(weak_map->Get(local1)->IsUndefined());
weak_map->Set(local1, value);
CHECK(weak_map->Has(local1));
CHECK(value->Equals(weak_map->Get(local1)));
WeakCallCounter counter(1234);
WeakCallCounterAndPersistent<Value> o1(&counter);
WeakCallCounterAndPersistent<Value> o2(&counter);
WeakCallCounterAndPersistent<Value> s1(&counter);
{
HandleScope scope(isolate);
Local<v8::Object> obj1 = v8::Object::New(isolate);
Local<v8::Object> obj2 = v8::Object::New(isolate);
Local<v8::Symbol> sym1 = v8::Symbol::New(isolate);
weak_map->Set(obj1, value);
weak_map->Set(obj2, value);
weak_map->Set(sym1, value);
o1.handle.Reset(isolate, obj1);
o2.handle.Reset(isolate, obj2);
s1.handle.Reset(isolate, sym1);
CHECK(weak_map->Has(local1));
CHECK(weak_map->Has(obj1));
CHECK(weak_map->Has(obj2));
CHECK(weak_map->Has(sym1));
CHECK(value->Equals(weak_map->Get(local1)));
CHECK(value->Equals(weak_map->Get(obj1)));
CHECK(value->Equals(weak_map->Get(obj2)));
CHECK(value->Equals(weak_map->Get(sym1)));
}
CcTest::heap()->CollectAllGarbage(TestHeap::Heap::kNoGCFlags);
{
HandleScope scope(isolate);
CHECK(value->Equals(weak_map->Get(local1)));
CHECK(value->Equals(weak_map->Get(Local<Value>::New(isolate, o1.handle))));
CHECK(value->Equals(weak_map->Get(Local<Value>::New(isolate, o2.handle))));
CHECK(value->Equals(weak_map->Get(Local<Value>::New(isolate, s1.handle))));
}
o1.handle.SetWeak(&o1, &WeakPointerCallback);
o2.handle.SetWeak(&o2, &WeakPointerCallback);
s1.handle.SetWeak(&s1, &WeakPointerCallback);
CcTest::heap()->CollectAllGarbage(TestHeap::Heap::kNoGCFlags);
CHECK_EQ(3, counter.NumberOfWeakCalls());
CHECK(o1.handle.IsEmpty());
CHECK(o2.handle.IsEmpty());
CHECK(s1.handle.IsEmpty());
CHECK(value->Equals(weak_map->Get(local1)));
CHECK(weak_map->Delete(local1));
CHECK(!weak_map->Has(local1));
CHECK(weak_map->Get(local1)->IsUndefined());
}
THREADED_TEST(GetSetProperty) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
......
......@@ -42,10 +42,7 @@ static Isolate* GetIsolateFrom(LocalContext* context) {
static Handle<JSWeakMap> AllocateJSWeakMap(Isolate* isolate) {
Factory* factory = isolate->factory();
Handle<Map> map = factory->NewMap(JS_WEAK_MAP_TYPE, JSWeakMap::kSize);
Handle<JSObject> weakmap_obj = factory->NewJSObjectFromMap(map);
Handle<JSWeakMap> weakmap(JSWeakMap::cast(*weakmap_obj));
Handle<JSWeakMap> weakmap = isolate->factory()->NewJSWeakMap();
// Do not leak handles for the hash table, it would make entries strong.
{
HandleScope scope(isolate);
......@@ -55,16 +52,6 @@ static Handle<JSWeakMap> AllocateJSWeakMap(Isolate* isolate) {
return weakmap;
}
static void PutIntoWeakMap(Handle<JSWeakMap> weakmap,
Handle<JSObject> key,
Handle<Object> value) {
Handle<ObjectHashTable> table = ObjectHashTable::Put(
Handle<ObjectHashTable>(ObjectHashTable::cast(weakmap->table())),
Handle<JSObject>(JSObject::cast(*key)),
value);
weakmap->set_table(*table);
}
static int NumberOfWeakCalls = 0;
static void WeakPointerCallback(
const v8::WeakCallbackData<v8::Value, void>& data) {
......@@ -102,8 +89,9 @@ TEST(Weakness) {
HandleScope scope(isolate);
Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
PutIntoWeakMap(weakmap, Handle<JSObject>(JSObject::cast(*key)), object);
PutIntoWeakMap(weakmap, object, Handle<Smi>(Smi::FromInt(23), isolate));
Handle<Smi> smi(Smi::FromInt(23), isolate);
Runtime::WeakCollectionSet(weakmap, key, object);
Runtime::WeakCollectionSet(weakmap, object, smi);
}
CHECK_EQ(2, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
......@@ -157,7 +145,8 @@ TEST(Shrinking) {
Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
for (int i = 0; i < 32; i++) {
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
PutIntoWeakMap(weakmap, object, Handle<Smi>(Smi::FromInt(i), isolate));
Handle<Smi> smi(Smi::FromInt(i), isolate);
Runtime::WeakCollectionSet(weakmap, object, smi);
}
}
......@@ -204,7 +193,7 @@ TEST(Regress2060a) {
Handle<JSObject> object = factory->NewJSObject(function, TENURED);
CHECK(!heap->InNewSpace(object->address()));
CHECK(!first_page->Contains(object->address()));
PutIntoWeakMap(weakmap, key, object);
Runtime::WeakCollectionSet(weakmap, key, object);
}
}
......@@ -244,9 +233,8 @@ TEST(Regress2060b) {
}
Handle<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
for (int i = 0; i < 32; i++) {
PutIntoWeakMap(weakmap,
keys[i],
Handle<Smi>(Smi::FromInt(i), isolate));
Handle<Smi> smi(Smi::FromInt(i), isolate);
Runtime::WeakCollectionSet(weakmap, keys[i], smi);
}
// Force compacting garbage collection. The subsequent collections are used
......
......@@ -55,16 +55,6 @@ static Handle<JSWeakSet> AllocateJSWeakSet(Isolate* isolate) {
return weakset;
}
static void PutIntoWeakSet(Handle<JSWeakSet> weakset,
Handle<JSObject> key,
Handle<Object> value) {
Handle<ObjectHashTable> table = ObjectHashTable::Put(
Handle<ObjectHashTable>(ObjectHashTable::cast(weakset->table())),
Handle<JSObject>(JSObject::cast(*key)),
value);
weakset->set_table(*table);
}
static int NumberOfWeakCalls = 0;
static void WeakPointerCallback(
const v8::WeakCallbackData<v8::Value, void>& data) {
......@@ -100,9 +90,8 @@ TEST(WeakSet_Weakness) {
// Put entry into weak set.
{
HandleScope scope(isolate);
PutIntoWeakSet(weakset,
Handle<JSObject>(JSObject::cast(*key)),
Handle<Smi>(Smi::FromInt(23), isolate));
Handle<Smi> smi(Smi::FromInt(23), isolate);
Runtime::WeakCollectionSet(weakset, key, smi);
}
CHECK_EQ(1, ObjectHashTable::cast(weakset->table())->NumberOfElements());
......@@ -156,7 +145,8 @@ TEST(WeakSet_Shrinking) {
Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
for (int i = 0; i < 32; i++) {
Handle<JSObject> object = factory->NewJSObjectFromMap(map);
PutIntoWeakSet(weakset, object, Handle<Smi>(Smi::FromInt(i), isolate));
Handle<Smi> smi(Smi::FromInt(i), isolate);
Runtime::WeakCollectionSet(weakset, object, smi);
}
}
......@@ -203,7 +193,7 @@ TEST(WeakSet_Regress2060a) {
Handle<JSObject> object = factory->NewJSObject(function, TENURED);
CHECK(!heap->InNewSpace(object->address()));
CHECK(!first_page->Contains(object->address()));
PutIntoWeakSet(weakset, key, object);
Runtime::WeakCollectionSet(weakset, key, object);
}
}
......@@ -243,9 +233,8 @@ TEST(WeakSet_Regress2060b) {
}
Handle<JSWeakSet> weakset = AllocateJSWeakSet(isolate);
for (int i = 0; i < 32; i++) {
PutIntoWeakSet(weakset,
keys[i],
Handle<Smi>(Smi::FromInt(i), isolate));
Handle<Smi> smi(Smi::FromInt(i), isolate);
Runtime::WeakCollectionSet(weakset, keys[i], smi);
}
// Force compacting garbage collection. The subsequent collections are used
......
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