Chromium Code Reviews| Index: test/cctest/test-api.cc |
| diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc |
| index 2282c2d9b9563b7d8a2b1c63548d6cc0824c1e80..848f5135ca643cb1ef5b1343f6a95dd5a7290402 100644 |
| --- a/test/cctest/test-api.cc |
| +++ b/test/cctest/test-api.cc |
| @@ -32,8 +32,8 @@ |
| #include "api.h" |
| #include "compilation-cache.h" |
| #include "execution.h" |
| -#include "snapshot.h" |
| #include "platform.h" |
| +#include "snapshot.h" |
| #include "top.h" |
| #include "cctest.h" |
| @@ -2915,84 +2915,162 @@ THREADED_TEST(ErrorWithMissingScriptInfo) { |
| } |
| -int global_index = 0; |
| +static void WeakFactoryCallback(v8::Persistent<v8::Value> object, void* data); |
| -class Snorkel { |
| +class WeakFactory { |
| public: |
| - Snorkel() { index_ = global_index++; } |
| - int index_; |
| -}; |
| + enum Mode { |
| + FACTORY_WEAK, |
| + FACTORY_REALLY_WEAK |
| + }; |
| -class Whammy { |
| - public: |
| - Whammy() { |
| - cursor_ = 0; |
| + explicit WeakFactory(Mode mode) |
| + : mode_(mode), |
| + objects_(8), |
| + template_(v8::Persistent<v8::FunctionTemplate>::New( |
| + v8::FunctionTemplate::New())), |
| + instance_template_(v8::Persistent<v8::ObjectTemplate>::New( |
| + template_->InstanceTemplate())) { } |
| + |
| + ~WeakFactory() { |
| + for (int i = 0; i < objects_.length(); ++i) { |
| + objects_[i].Dispose(); |
| + } |
| } |
| - ~Whammy() { |
| - script_.Dispose(); |
| + |
| + v8::Persistent<v8::Object> CreateObject() { |
| + v8::Persistent<v8::Object> object = |
| + v8::Persistent<v8::Object>::New(instance_template_->NewInstance()); |
| + if (mode_ == FACTORY_WEAK) { |
| + object.MakeWeak(this, &WeakFactoryCallback); |
| + } else if (mode_ == FACTORY_REALLY_WEAK) { |
| + object.MakeReallyWeak(this, &WeakFactoryCallback); |
| + } else { |
| + UNREACHABLE(); |
| + } |
| + objects_.Add(object); |
| + return object; |
| + } |
| + |
| + void Return(v8::Persistent<v8::Value> object) { |
| + for (int i = 0; i < objects_.length(); ++i) { |
| + if (objects_[i] == object) { |
| + object.Dispose(); |
| + objects_.Remove(i); |
| + return; |
| + } |
| + } |
| + CHECK(false); |
|
Christian Plesner Hansen
2009/10/06 13:47:21
UNREACHABLE()?
|
| } |
| - v8::Handle<Script> getScript() { |
| - if (script_.IsEmpty()) |
| - script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo")); |
| - return Local<Script>(*script_); |
| + |
| + int NumLive() const { return objects_.length(); } |
| + |
| + int NumInHeap() const { |
| + int counter = 0; |
| + i::HeapIterator iter; |
| + while (iter.has_next()) { |
| + i::HeapObject* object = iter.next(); |
| + if (object->IsInstanceOf(*v8::Utils::OpenHandle(*template_))) { |
| + ++counter; |
| + } |
| + } |
| + return counter; |
| } |
| - public: |
| - static const int kObjectCount = 256; |
| - int cursor_; |
| - v8::Persistent<v8::Object> objects_[kObjectCount]; |
| - v8::Persistent<Script> script_; |
| + private: |
| + const Mode mode_; |
|
Christian Plesner Hansen
2009/10/06 13:47:21
Why const?
|
| + i::List<v8::Persistent<v8::Value> > objects_; |
| + v8::Persistent<v8::FunctionTemplate> template_; |
| + v8::Persistent<v8::ObjectTemplate> instance_template_; |
| }; |
| -static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) { |
| - Snorkel* snorkel = reinterpret_cast<Snorkel*>(data); |
| - delete snorkel; |
| - obj.ClearWeak(); |
| +static void WeakFactoryCallback(v8::Persistent<v8::Value> object, void* data) { |
| + WeakFactory* factory = reinterpret_cast<WeakFactory*>(data); |
| + factory->Return(object); |
| } |
| -v8::Handle<Value> WhammyPropertyGetter(Local<String> name, |
| - const AccessorInfo& info) { |
| - Whammy* whammy = |
| - static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); |
| +static v8::Handle<v8::Value> WeakFactoryCreateObject( |
| + const v8::Arguments& args) { |
| + WeakFactory* factory = |
| + reinterpret_cast<WeakFactory*>(v8::External::Unwrap(args.Data())); |
| + return factory->CreateObject(); |
| +} |
| + |
| +static const char* const kTestWeakCode = |
| + "var last = null;" |
| + "(function() {" |
| + " for (var i = 0; i < 10000; i++) {" |
| + " var obj = createObject();" |
| + " obj.next = last;" |
| + " last = obj;" |
| + " }" |
| + "})();"; |
| - v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_]; |
| +THREADED_TEST(WeakReference) { |
| + v8::HandleScope handle_scope; |
| + WeakFactory* factory = new WeakFactory(WeakFactory::FACTORY_WEAK); |
| - v8::Handle<v8::Object> obj = v8::Object::New(); |
| - v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj); |
| - if (!prev.IsEmpty()) { |
| - prev->Set(v8_str("next"), obj); |
| - prev.MakeWeak(new Snorkel(), &HandleWeakReference); |
| - whammy->objects_[whammy->cursor_].Clear(); |
| - } |
| - whammy->objects_[whammy->cursor_] = global; |
| - whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount; |
| - return whammy->getScript()->Run(); |
| + const char* extension_list[] = { "v8/gc" }; |
| + v8::ExtensionConfiguration extensions(1, extension_list); |
| + v8::Persistent<Context> context = Context::New(&extensions); |
| + Context::Scope context_scope(context); |
| + |
| + v8::Handle<v8::FunctionTemplate> function_templ = v8::FunctionTemplate::New( |
| + &WeakFactoryCreateObject, v8::External::Wrap(factory)); |
| + context->Global()->Set(v8_str("createObject"), function_templ->GetFunction()); |
| + |
| + // Create objects held by a variable. |
| + CompileRun(kTestWeakCode); |
| + CHECK_EQ(10000, factory->NumLive()); |
| + CHECK_EQ(10000, factory->NumInHeap()); |
| + |
| + // Make sure GC doesn't collect them. |
| + CompileRun("gc(); gc();"); |
| + CHECK_EQ(10000, factory->NumLive()); |
| + CHECK_EQ(10000, factory->NumInHeap()); |
| + |
| + // Make sure GC realizes they are no longer reachable and invokes |
| + // weak callbacks after the variable is reset. |
| + CompileRun("last = null; gc();"); |
| + CHECK_EQ(0, factory->NumLive()); |
| + CHECK_EQ(10000, factory->NumInHeap()); |
| + |
| + // Next GC after callbacks should collect the objects. |
| + CompileRun("gc();"); |
| + CHECK_EQ(0, factory->NumLive()); |
| + CHECK_EQ(0, factory->NumInHeap()); |
| + |
| + context.Dispose(); |
| } |
| -THREADED_TEST(WeakReference) { |
| +THREADED_TEST(ReallyWeakReference) { |
| v8::HandleScope handle_scope; |
| - v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New(); |
| - templ->SetNamedPropertyHandler(WhammyPropertyGetter, |
| - 0, 0, 0, 0, |
| - v8::External::New(new Whammy())); |
| + WeakFactory* factory = new WeakFactory(WeakFactory::FACTORY_REALLY_WEAK); |
| + |
| const char* extension_list[] = { "v8/gc" }; |
| v8::ExtensionConfiguration extensions(1, extension_list); |
| v8::Persistent<Context> context = Context::New(&extensions); |
| Context::Scope context_scope(context); |
| - v8::Handle<v8::Object> interceptor = templ->NewInstance(); |
| - context->Global()->Set(v8_str("whammy"), interceptor); |
| - const char* code = |
| - "var last;" |
| - "for (var i = 0; i < 10000; i++) {" |
| - " var obj = whammy.length;" |
| - " if (last) last.next = obj;" |
| - " last = obj;" |
| - "}" |
| - "gc();" |
| - "4"; |
| - v8::Handle<Value> result = CompileRun(code); |
| - CHECK_EQ(4.0, result->NumberValue()); |
| + v8::Handle<v8::FunctionTemplate> function_templ = v8::FunctionTemplate::New( |
| + &WeakFactoryCreateObject, v8::External::Wrap(factory)); |
| + context->Global()->Set(v8_str("createObject"), function_templ->GetFunction()); |
| + |
| + // See the comments above in WeakReference test. |
| + |
| + CompileRun(kTestWeakCode); |
| + CHECK_EQ(10000, factory->NumLive()); |
| + CHECK_EQ(10000, factory->NumInHeap()); |
| + |
| + CompileRun("gc(); gc();"); |
| + CHECK_EQ(10000, factory->NumLive()); |
| + CHECK_EQ(10000, factory->NumInHeap()); |
| + |
| + // Unlike above objects should be immediately collected when they |
| + // are unreachable. |
| + CompileRun("last = null; gc();"); |
| + CHECK_EQ(0, factory->NumLive()); |
| + CHECK_EQ(0, factory->NumInHeap()); |
| context.Dispose(); |
| } |