| Index: test/cctest/test-api.cc
|
| diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
|
| index 22e207785888f2857db01f6e20486a8a64c216d4..d5e838ebe09618a6dbc3dcdd567d5d0136482cc2 100644
|
| --- a/test/cctest/test-api.cc
|
| +++ b/test/cctest/test-api.cc
|
| @@ -3318,8 +3318,9 @@ class WeakCallCounter {
|
| };
|
|
|
|
|
| +template<typename T>
|
| static void WeakPointerCallback(v8::Isolate* isolate,
|
| - Persistent<Value>* handle,
|
| + Persistent<T>* handle,
|
| WeakCallCounter* counter) {
|
| CHECK_EQ(1234, counter->id());
|
| counter->increment();
|
| @@ -3327,7 +3328,8 @@ static void WeakPointerCallback(v8::Isolate* isolate,
|
| }
|
|
|
|
|
| -static UniqueId MakeUniqueId(const Persistent<Value>& p) {
|
| +template<typename T>
|
| +static UniqueId MakeUniqueId(const Persistent<T>& p) {
|
| return UniqueId(reinterpret_cast<uintptr_t>(*v8::Utils::OpenPersistent(p)));
|
| }
|
|
|
| @@ -3425,6 +3427,97 @@ THREADED_TEST(ApiObjectGroups) {
|
| }
|
|
|
|
|
| +THREADED_TEST(ApiObjectGroupsForSubtypes) {
|
| + LocalContext env;
|
| + v8::Isolate* iso = env->GetIsolate();
|
| + HandleScope scope(iso);
|
| +
|
| + Persistent<Object> g1s1;
|
| + Persistent<String> g1s2;
|
| + Persistent<String> g1c1;
|
| + Persistent<Object> g2s1;
|
| + Persistent<String> g2s2;
|
| + Persistent<String> g2c1;
|
| +
|
| + WeakCallCounter counter(1234);
|
| +
|
| + {
|
| + HandleScope scope(iso);
|
| + g1s1.Reset(iso, Object::New());
|
| + g1s2.Reset(iso, String::New("foo1"));
|
| + g1c1.Reset(iso, String::New("foo2"));
|
| + g1s1.MakeWeak(&counter, &WeakPointerCallback);
|
| + g1s2.MakeWeak(&counter, &WeakPointerCallback);
|
| + g1c1.MakeWeak(&counter, &WeakPointerCallback);
|
| +
|
| + g2s1.Reset(iso, Object::New());
|
| + g2s2.Reset(iso, String::New("foo3"));
|
| + g2c1.Reset(iso, String::New("foo4"));
|
| + g2s1.MakeWeak(&counter, &WeakPointerCallback);
|
| + g2s2.MakeWeak(&counter, &WeakPointerCallback);
|
| + g2c1.MakeWeak(&counter, &WeakPointerCallback);
|
| + }
|
| +
|
| + Persistent<Value> root(iso, g1s1); // make a root.
|
| +
|
| + // Connect group 1 and 2, make a cycle.
|
| + {
|
| + HandleScope scope(iso);
|
| + CHECK(Local<Object>::New(iso, g1s1)->Set(0, Local<Object>::New(iso, g2s1)));
|
| + CHECK(Local<Object>::New(iso, g2s1)->Set(0, Local<Object>::New(iso, g1s1)));
|
| + }
|
| +
|
| + {
|
| + UniqueId id1 = MakeUniqueId(g1s1);
|
| + UniqueId id2 = MakeUniqueId(g2s2);
|
| + iso->SetObjectGroupId(g1s1, id1);
|
| + iso->SetObjectGroupId(g1s2, id1);
|
| + iso->SetReference(g1s1, g1c1);
|
| + iso->SetObjectGroupId(g2s1, id2);
|
| + iso->SetObjectGroupId(g2s2, id2);
|
| + iso->SetReferenceFromGroup(id2, g2c1);
|
| + }
|
| + // Do a single full GC, ensure incremental marking is stopped.
|
| + v8::internal::Heap* heap = reinterpret_cast<v8::internal::Isolate*>(
|
| + iso)->heap();
|
| + heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
|
| +
|
| + // All object should be alive.
|
| + CHECK_EQ(0, counter.NumberOfWeakCalls());
|
| +
|
| + // Weaken the root.
|
| + root.MakeWeak(&counter, &WeakPointerCallback);
|
| + // But make children strong roots---all the objects (except for children)
|
| + // should be collectable now.
|
| + g1c1.ClearWeak();
|
| + g2c1.ClearWeak();
|
| +
|
| + // Groups are deleted, rebuild groups.
|
| + {
|
| + UniqueId id1 = MakeUniqueId(g1s1);
|
| + UniqueId id2 = MakeUniqueId(g2s2);
|
| + iso->SetObjectGroupId(g1s1, id1);
|
| + iso->SetObjectGroupId(g1s2, id1);
|
| + iso->SetReference(g1s1, g1c1);
|
| + iso->SetObjectGroupId(g2s1, id2);
|
| + iso->SetObjectGroupId(g2s2, id2);
|
| + iso->SetReferenceFromGroup(id2, g2c1);
|
| + }
|
| +
|
| + heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
|
| +
|
| + // All objects should be gone. 5 global handles in total.
|
| + CHECK_EQ(5, counter.NumberOfWeakCalls());
|
| +
|
| + // And now make children weak again and collect them.
|
| + g1c1.MakeWeak(&counter, &WeakPointerCallback);
|
| + g2c1.MakeWeak(&counter, &WeakPointerCallback);
|
| +
|
| + heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
|
| + CHECK_EQ(7, counter.NumberOfWeakCalls());
|
| +}
|
| +
|
| +
|
| THREADED_TEST(ApiObjectGroupsCycle) {
|
| LocalContext env;
|
| v8::Isolate* iso = env->GetIsolate();
|
| @@ -14323,129 +14416,6 @@ THREADED_TEST(CrossContextNew) {
|
| }
|
|
|
|
|
| -class RegExpInterruptTest {
|
| - public:
|
| - RegExpInterruptTest() : block_(0) {}
|
| - ~RegExpInterruptTest() {}
|
| - void RunTest() {
|
| - gc_count_ = 0;
|
| - gc_during_regexp_ = 0;
|
| - regexp_success_ = false;
|
| - gc_success_ = false;
|
| - GCThread gc_thread(this);
|
| - gc_thread.Start();
|
| - v8::Isolate* isolate = CcTest::isolate();
|
| - v8::Locker::StartPreemption(isolate, 1);
|
| -
|
| - LongRunningRegExp();
|
| - {
|
| - v8::Unlocker unlock(isolate);
|
| - gc_thread.Join();
|
| - }
|
| - v8::Locker::StopPreemption(isolate);
|
| - CHECK(regexp_success_);
|
| - CHECK(gc_success_);
|
| - }
|
| -
|
| - private:
|
| - // Number of garbage collections required.
|
| - static const int kRequiredGCs = 5;
|
| -
|
| - class GCThread : public i::Thread {
|
| - public:
|
| - explicit GCThread(RegExpInterruptTest* test)
|
| - : Thread("GCThread"), test_(test) {}
|
| - virtual void Run() {
|
| - test_->CollectGarbage();
|
| - }
|
| - private:
|
| - RegExpInterruptTest* test_;
|
| - };
|
| -
|
| - void CollectGarbage() {
|
| - block_.Wait();
|
| - while (gc_during_regexp_ < kRequiredGCs) {
|
| - {
|
| - v8::Locker lock(CcTest::isolate());
|
| - v8::Isolate::Scope isolate_scope(CcTest::isolate());
|
| - // TODO(lrn): Perhaps create some garbage before collecting.
|
| - CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
|
| - gc_count_++;
|
| - }
|
| - i::OS::Sleep(1);
|
| - }
|
| - gc_success_ = true;
|
| - }
|
| -
|
| - void LongRunningRegExp() {
|
| - block_.Signal(); // Enable garbage collection thread on next preemption.
|
| - int rounds = 0;
|
| - while (gc_during_regexp_ < kRequiredGCs) {
|
| - int gc_before = gc_count_;
|
| - {
|
| - // Match 15-30 "a"'s against 14 and a "b".
|
| - const char* c_source =
|
| - "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
|
| - ".exec('aaaaaaaaaaaaaaab') === null";
|
| - Local<String> source = String::New(c_source);
|
| - Local<Script> script = Script::Compile(source);
|
| - Local<Value> result = script->Run();
|
| - if (!result->BooleanValue()) {
|
| - gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
|
| - return;
|
| - }
|
| - }
|
| - {
|
| - // Match 15-30 "a"'s against 15 and a "b".
|
| - const char* c_source =
|
| - "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
|
| - ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
|
| - Local<String> source = String::New(c_source);
|
| - Local<Script> script = Script::Compile(source);
|
| - Local<Value> result = script->Run();
|
| - if (!result->BooleanValue()) {
|
| - gc_during_regexp_ = kRequiredGCs;
|
| - return;
|
| - }
|
| - }
|
| - int gc_after = gc_count_;
|
| - gc_during_regexp_ += gc_after - gc_before;
|
| - rounds++;
|
| - i::OS::Sleep(1);
|
| - }
|
| - regexp_success_ = true;
|
| - }
|
| -
|
| - i::Semaphore block_;
|
| - int gc_count_;
|
| - int gc_during_regexp_;
|
| - bool regexp_success_;
|
| - bool gc_success_;
|
| -};
|
| -
|
| -
|
| -// Test that a regular expression execution can be interrupted and
|
| -// survive a garbage collection.
|
| -TEST(RegExpInterruption) {
|
| - v8::Locker lock(CcTest::isolate());
|
| - v8::HandleScope scope(CcTest::isolate());
|
| - Local<Context> local_env;
|
| - {
|
| - LocalContext env;
|
| - local_env = env.local();
|
| - }
|
| -
|
| - // Local context should still be live.
|
| - CHECK(!local_env.IsEmpty());
|
| - local_env->Enter();
|
| -
|
| - // Should complete without problems.
|
| - RegExpInterruptTest().RunTest();
|
| -
|
| - local_env->Exit();
|
| -}
|
| -
|
| -
|
| class ApplyInterruptTest {
|
| public:
|
| ApplyInterruptTest() : block_(0) {}
|
| @@ -14735,144 +14705,79 @@ TEST(CompileExternalTwoByteSource) {
|
| }
|
|
|
|
|
| -class RegExpStringModificationTest {
|
| - public:
|
| - RegExpStringModificationTest()
|
| - : block_(0),
|
| - morphs_(0),
|
| - morphs_during_regexp_(0),
|
| - ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
|
| - uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
|
| - ~RegExpStringModificationTest() {}
|
| - void RunTest() {
|
| - v8::Isolate* isolate = CcTest::isolate();
|
| - i::Factory* factory = CcTest::i_isolate()->factory();
|
| +#ifndef V8_INTERPRETED_REGEXP
|
|
|
| - regexp_success_ = false;
|
| - morph_success_ = false;
|
| +struct RegExpInterruptionData {
|
| + int loop_count;
|
| + UC16VectorResource* string_resource;
|
| + v8::Persistent<v8::String> string;
|
| +} regexp_interruption_data;
|
|
|
| - // Initialize the contents of two_byte_content_ to be a uc16 representation
|
| - // of "aaaaaaaaaaaaaab".
|
| - for (int i = 0; i < 14; i++) {
|
| - two_byte_content_[i] = 'a';
|
| - }
|
| - two_byte_content_[14] = 'b';
|
| -
|
| - // Create the input string for the regexp - the one we are going to change
|
| - // properties of.
|
| - input_ = factory->NewExternalStringFromAscii(&ascii_resource_);
|
| -
|
| - // Inject the input as a global variable.
|
| - i::Handle<i::String> input_name =
|
| - factory->NewStringFromAscii(i::Vector<const char>("input", 5));
|
| - i::JSReceiver::SetProperty(
|
| - i::handle(CcTest::i_isolate()->native_context()->global_object()),
|
| - input_name,
|
| - input_,
|
| - NONE,
|
| - i::kNonStrictMode);
|
| -
|
| - MorphThread morph_thread(this);
|
| - morph_thread.Start();
|
| - v8::Locker::StartPreemption(isolate, 1);
|
| - LongRunningRegExp();
|
| - {
|
| - v8::Unlocker unlock(isolate);
|
| - morph_thread.Join();
|
| +
|
| +class RegExpInterruptionThread : public i::Thread {
|
| + public:
|
| + explicit RegExpInterruptionThread(v8::Isolate* isolate)
|
| + : Thread("TimeoutThread"), isolate_(isolate) {}
|
| +
|
| + virtual void Run() {
|
| + for (regexp_interruption_data.loop_count = 0;
|
| + regexp_interruption_data.loop_count < 7;
|
| + regexp_interruption_data.loop_count++) {
|
| + i::OS::Sleep(50); // Wait a bit before requesting GC.
|
| + reinterpret_cast<i::Isolate*>(isolate_)->stack_guard()->RequestGC();
|
| }
|
| - v8::Locker::StopPreemption(isolate);
|
| - CHECK(regexp_success_);
|
| - CHECK(morph_success_);
|
| + i::OS::Sleep(50); // Wait a bit before terminating.
|
| + v8::V8::TerminateExecution(isolate_);
|
| }
|
|
|
| private:
|
| - // Number of string modifications required.
|
| - static const int kRequiredModifications = 5;
|
| - static const int kMaxModifications = 100;
|
| + v8::Isolate* isolate_;
|
| +};
|
|
|
| - class MorphThread : public i::Thread {
|
| - public:
|
| - explicit MorphThread(RegExpStringModificationTest* test)
|
| - : Thread("MorphThread"), test_(test) {}
|
| - virtual void Run() {
|
| - test_->MorphString();
|
| - }
|
| - private:
|
| - RegExpStringModificationTest* test_;
|
| - };
|
|
|
| - void MorphString() {
|
| - block_.Wait();
|
| - while (morphs_during_regexp_ < kRequiredModifications &&
|
| - morphs_ < kMaxModifications) {
|
| - {
|
| - v8::Locker lock(CcTest::isolate());
|
| - v8::Isolate::Scope isolate_scope(CcTest::isolate());
|
| - // Swap string between ascii and two-byte representation.
|
| - i::String* string = *input_;
|
| - MorphAString(string, &ascii_resource_, &uc16_resource_);
|
| - morphs_++;
|
| - }
|
| - i::OS::Sleep(1);
|
| - }
|
| - morph_success_ = true;
|
| - }
|
| +void RunBeforeGC(v8::GCType type, v8::GCCallbackFlags flags) {
|
| + if (regexp_interruption_data.loop_count != 2) return;
|
| + v8::HandleScope scope(CcTest::isolate());
|
| + v8::Local<v8::String> string = v8::Local<v8::String>::New(
|
| + CcTest::isolate(), regexp_interruption_data.string);
|
| + string->MakeExternal(regexp_interruption_data.string_resource);
|
| +}
|
|
|
| - void LongRunningRegExp() {
|
| - block_.Signal(); // Enable morphing thread on next preemption.
|
| - while (morphs_during_regexp_ < kRequiredModifications &&
|
| - morphs_ < kMaxModifications) {
|
| - int morphs_before = morphs_;
|
| - {
|
| - v8::HandleScope scope(CcTest::isolate());
|
| - // Match 15-30 "a"'s against 14 and a "b".
|
| - const char* c_source =
|
| - "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
|
| - ".exec(input) === null";
|
| - Local<String> source = String::New(c_source);
|
| - Local<Script> script = Script::Compile(source);
|
| - Local<Value> result = script->Run();
|
| - CHECK(result->IsTrue());
|
| - }
|
| - int morphs_after = morphs_;
|
| - morphs_during_regexp_ += morphs_after - morphs_before;
|
| - }
|
| - regexp_success_ = true;
|
| - }
|
|
|
| - i::uc16 two_byte_content_[15];
|
| - i::Semaphore block_;
|
| - int morphs_;
|
| - int morphs_during_regexp_;
|
| - bool regexp_success_;
|
| - bool morph_success_;
|
| - i::Handle<i::String> input_;
|
| - AsciiVectorResource ascii_resource_;
|
| - UC16VectorResource uc16_resource_;
|
| -};
|
| +// Test that RegExp execution can be interrupted. Specifically, we test
|
| +// * interrupting with GC
|
| +// * turn the subject string from one-byte internal to two-byte external string
|
| +// * force termination
|
| +TEST(RegExpInterruption) {
|
| + v8::HandleScope scope(CcTest::isolate());
|
| + LocalContext env;
|
|
|
| + RegExpInterruptionThread timeout_thread(CcTest::isolate());
|
|
|
| -// Test that a regular expression execution can be interrupted and
|
| -// the string changed without failing.
|
| -TEST(RegExpStringModification) {
|
| - v8::Locker lock(CcTest::isolate());
|
| - v8::HandleScope scope(CcTest::isolate());
|
| - Local<Context> local_env;
|
| - {
|
| - LocalContext env;
|
| - local_env = env.local();
|
| - }
|
| + v8::V8::AddGCPrologueCallback(RunBeforeGC);
|
| + static const char* ascii_content = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
| + i::uc16* uc16_content = AsciiToTwoByteString(ascii_content);
|
| + v8::Local<v8::String> string = v8_str(ascii_content);
|
|
|
| - // Local context should still be live.
|
| - CHECK(!local_env.IsEmpty());
|
| - local_env->Enter();
|
| + CcTest::global()->Set(v8_str("a"), string);
|
| + regexp_interruption_data.string.Reset(CcTest::isolate(), string);
|
| + regexp_interruption_data.string_resource = new UC16VectorResource(
|
| + i::Vector<const i::uc16>(uc16_content, i::StrLength(ascii_content)));
|
|
|
| - // Should complete without problems.
|
| - RegExpStringModificationTest().RunTest();
|
| + v8::TryCatch try_catch;
|
| + timeout_thread.Start();
|
|
|
| - local_env->Exit();
|
| + CompileRun("/((a*)*)*b/.exec(a)");
|
| + CHECK(try_catch.HasTerminated());
|
| +
|
| + timeout_thread.Join();
|
| +
|
| + delete regexp_interruption_data.string_resource;
|
| + regexp_interruption_data.string.Dispose();
|
| }
|
|
|
| +#endif // V8_INTERPRETED_REGEXP
|
| +
|
|
|
| // Test that we cannot set a property on the global object if there
|
| // is a read-only property in the prototype chain.
|
| @@ -17599,6 +17504,70 @@ THREADED_TEST(FunctionGetInferredName) {
|
| }
|
|
|
|
|
| +THREADED_TEST(FunctionGetDisplayName) {
|
| + LocalContext env;
|
| + v8::HandleScope scope(env->GetIsolate());
|
| + const char* code = "var error = false;"
|
| + "function a() { this.x = 1; };"
|
| + "a.displayName = 'display_a';"
|
| + "var b = (function() {"
|
| + " var f = function() { this.x = 2; };"
|
| + " f.displayName = 'display_b';"
|
| + " return f;"
|
| + "})();"
|
| + "var c = function() {};"
|
| + "c.__defineGetter__('displayName', function() {"
|
| + " error = true;"
|
| + " throw new Error();"
|
| + "});"
|
| + "function d() {};"
|
| + "d.__defineGetter__('displayName', function() {"
|
| + " error = true;"
|
| + " return 'wrong_display_name';"
|
| + "});"
|
| + "function e() {};"
|
| + "e.displayName = 'wrong_display_name';"
|
| + "e.__defineSetter__('displayName', function() {"
|
| + " error = true;"
|
| + " throw new Error();"
|
| + "});"
|
| + "function f() {};"
|
| + "f.displayName = { 'foo': 6, toString: function() {"
|
| + " error = true;"
|
| + " return 'wrong_display_name';"
|
| + "}};"
|
| + "var g = function() {"
|
| + " arguments.callee.displayName = 'set_in_runtime';"
|
| + "}; g();"
|
| + ;
|
| + v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
|
| + v8::Script::Compile(v8::String::New(code), &origin)->Run();
|
| + v8::Local<v8::Value> error = env->Global()->Get(v8::String::New("error"));
|
| + v8::Local<v8::Function> a = v8::Local<v8::Function>::Cast(
|
| + env->Global()->Get(v8::String::New("a")));
|
| + v8::Local<v8::Function> b = v8::Local<v8::Function>::Cast(
|
| + env->Global()->Get(v8::String::New("b")));
|
| + v8::Local<v8::Function> c = v8::Local<v8::Function>::Cast(
|
| + env->Global()->Get(v8::String::New("c")));
|
| + v8::Local<v8::Function> d = v8::Local<v8::Function>::Cast(
|
| + env->Global()->Get(v8::String::New("d")));
|
| + v8::Local<v8::Function> e = v8::Local<v8::Function>::Cast(
|
| + env->Global()->Get(v8::String::New("e")));
|
| + v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
|
| + env->Global()->Get(v8::String::New("f")));
|
| + v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
|
| + env->Global()->Get(v8::String::New("g")));
|
| + CHECK_EQ(false, error->BooleanValue());
|
| + CHECK_EQ("display_a", *v8::String::Utf8Value(a->GetDisplayName()));
|
| + CHECK_EQ("display_b", *v8::String::Utf8Value(b->GetDisplayName()));
|
| + CHECK(c->GetDisplayName()->IsUndefined());
|
| + CHECK(d->GetDisplayName()->IsUndefined());
|
| + CHECK(e->GetDisplayName()->IsUndefined());
|
| + CHECK(f->GetDisplayName()->IsUndefined());
|
| + CHECK_EQ("set_in_runtime", *v8::String::Utf8Value(g->GetDisplayName()));
|
| +}
|
| +
|
| +
|
| THREADED_TEST(ScriptLineNumber) {
|
| LocalContext env;
|
| v8::HandleScope scope(env->GetIsolate());
|
| @@ -17632,6 +17601,23 @@ THREADED_TEST(ScriptColumnNumber) {
|
| }
|
|
|
|
|
| +THREADED_TEST(FunctionIsBuiltin) {
|
| + LocalContext env;
|
| + v8::HandleScope scope(env->GetIsolate());
|
| + v8::Local<v8::Function> f;
|
| + f = v8::Local<v8::Function>::Cast(CompileRun("Math.floor"));
|
| + CHECK(f->IsBuiltin());
|
| + f = v8::Local<v8::Function>::Cast(CompileRun("Object"));
|
| + CHECK(f->IsBuiltin());
|
| + f = v8::Local<v8::Function>::Cast(CompileRun("Object.__defineSetter__"));
|
| + CHECK(f->IsBuiltin());
|
| + f = v8::Local<v8::Function>::Cast(CompileRun("Array.prototype.toString"));
|
| + CHECK(f->IsBuiltin());
|
| + f = v8::Local<v8::Function>::Cast(CompileRun("function a() {}; a;"));
|
| + CHECK(!f->IsBuiltin());
|
| +}
|
| +
|
| +
|
| THREADED_TEST(FunctionGetScriptId) {
|
| LocalContext env;
|
| v8::HandleScope scope(env->GetIsolate());
|
|
|