Index: test/cctest/test-object-observe.cc |
diff --git a/test/cctest/test-object-observe.cc b/test/cctest/test-object-observe.cc |
index a096828ff66dd13234d67a176b62d1faf6d09efb..3e9dcd7711847a805adf4b9b095251edb7bd6b5f 100644 |
--- a/test/cctest/test-object-observe.cc |
+++ b/test/cctest/test-object-observe.cc |
@@ -613,3 +613,95 @@ TEST(GetNotifierFromSameOrigin) { |
CHECK(CompileRun("Object.getNotifier(obj)")->IsObject()); |
} |
} |
+ |
+ |
+static int GetGlobalObjectsCount() { |
+ CcTest::heap()->EnsureHeapIsIterable(); |
+ int count = 0; |
+ i::HeapIterator it(CcTest::heap()); |
+ for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) |
+ if (object->IsJSGlobalObject()) count++; |
+ return count; |
+} |
+ |
+ |
+static void CheckSurvivingGlobalObjectsCount(int expected) { |
+ // We need to collect all garbage twice to be sure that everything |
+ // has been collected. This is because inline caches are cleared in |
+ // the first garbage collection but some of the maps have already |
+ // been marked at that point. Therefore some of the maps are not |
+ // collected until the second garbage collection. |
+ CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); |
+ CcTest::heap()->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); |
+ int count = GetGlobalObjectsCount(); |
+#ifdef DEBUG |
+ if (count != expected) CcTest::heap()->TracePathToGlobal(); |
+#endif |
+ CHECK_EQ(expected, count); |
+} |
+ |
+ |
+TEST(DontLeakContextOnObserve) { |
+ HandleScope scope(CcTest::isolate()); |
+ Handle<Value> foo = String::NewFromUtf8(CcTest::isolate(), "foo"); |
+ LocalContext context(CcTest::isolate()); |
+ context->SetSecurityToken(foo); |
+ CompileRun("var obj = {};"); |
+ Handle<Value> object = CompileRun("obj"); |
+ { |
+ HandleScope scope(CcTest::isolate()); |
+ LocalContext context2(CcTest::isolate()); |
+ context2->SetSecurityToken(foo); |
+ context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), |
+ object); |
+ CompileRun("function observer() {};" |
+ "Object.observe(obj, observer, ['foo', 'bar', 'baz']);" |
+ "Object.unobserve(obj, observer);"); |
+ } |
+ |
+ v8::V8::ContextDisposedNotification(); |
+ CheckSurvivingGlobalObjectsCount(1); |
+} |
+ |
+ |
+TEST(DontLeakContextOnGetNotifier) { |
+ HandleScope scope(CcTest::isolate()); |
+ Handle<Value> foo = String::NewFromUtf8(CcTest::isolate(), "foo"); |
+ LocalContext context(CcTest::isolate()); |
+ context->SetSecurityToken(foo); |
+ CompileRun("var obj = {};"); |
+ Handle<Value> object = CompileRun("obj"); |
+ { |
+ HandleScope scope(CcTest::isolate()); |
+ LocalContext context2(CcTest::isolate()); |
+ context2->SetSecurityToken(foo); |
+ context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), |
+ object); |
+ CompileRun("Object.getNotifier(obj);"); |
+ } |
+ |
+ v8::V8::ContextDisposedNotification(); |
+ CheckSurvivingGlobalObjectsCount(1); |
+} |
+ |
+ |
+TEST(DontLeakContextOnNotifierPerformChange) { |
+ HandleScope scope(CcTest::isolate()); |
+ Handle<Value> foo = String::NewFromUtf8(CcTest::isolate(), "foo"); |
+ LocalContext context(CcTest::isolate()); |
+ context->SetSecurityToken(foo); |
+ CompileRun("var obj = {};"); |
+ Handle<Value> object = CompileRun("obj"); |
+ { |
+ HandleScope scope(CcTest::isolate()); |
+ LocalContext context2(CcTest::isolate()); |
+ context2->SetSecurityToken(foo); |
+ context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), |
+ object); |
+ CompileRun("var n = Object.getNotifier(obj);" |
+ "n.performChange('foo', function() {});"); |
+ } |
+ |
+ v8::V8::ContextDisposedNotification(); |
+ CheckSurvivingGlobalObjectsCount(1); |
+} |