Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2007-2008 the V8 project authors. All rights reserved. | 1 // Copyright 2007-2008 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 5667 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 5678 context1->Exit(); | 5678 context1->Exit(); |
| 5679 | 5679 |
| 5680 // Dispose the contexts to allow them to be garbage collected. | 5680 // Dispose the contexts to allow them to be garbage collected. |
| 5681 context0.Dispose(); | 5681 context0.Dispose(); |
| 5682 context1.Dispose(); | 5682 context1.Dispose(); |
| 5683 } | 5683 } |
| 5684 | 5684 |
| 5685 | 5685 |
| 5686 class RegExpInterruptTest { | 5686 class RegExpInterruptTest { |
| 5687 public: | 5687 public: |
| 5688 RegExpInterruptTest() : block_(NULL) {} | |
| 5689 ~RegExpInterruptTest() { delete block_; } | |
| 5688 void RunTest() { | 5690 void RunTest() { |
| 5689 block_ = i::OS::CreateSemaphore(0); | 5691 block_ = i::OS::CreateSemaphore(0); |
| 5690 gc_count_ = 0; | 5692 gc_count_ = 0; |
| 5691 gc_during_regexp_ = 0; | 5693 gc_during_regexp_ = 0; |
| 5692 regexp_success_ = false; | 5694 regexp_success_ = false; |
| 5693 gc_success_ = false; | 5695 gc_success_ = false; |
| 5694 GCThread gc_thread(this); | 5696 GCThread gc_thread(this); |
| 5695 gc_thread.Start(); | 5697 gc_thread.Start(); |
| 5696 v8::Locker::StartPreemption(1); | 5698 v8::Locker::StartPreemption(1); |
| 5697 | 5699 |
| 5698 LongRunningRegExp(); | 5700 LongRunningRegExp(); |
| 5699 { | 5701 { |
| 5700 v8::Unlocker unlock; | 5702 v8::Unlocker unlock; |
| 5701 gc_thread.Join(); | 5703 gc_thread.Join(); |
| 5702 } | 5704 } |
| 5703 v8::Locker::StopPreemption(); | 5705 v8::Locker::StopPreemption(); |
| 5704 CHECK(regexp_success_); | 5706 CHECK(regexp_success_); |
| 5705 CHECK(gc_success_); | 5707 CHECK(gc_success_); |
| 5706 } | 5708 } |
| 5707 RegExpInterruptTest() : block_(NULL) {} | |
| 5708 ~RegExpInterruptTest() { delete block_; } | |
| 5709 private: | 5709 private: |
| 5710 // Number of garbage collections required. | 5710 // Number of garbage collections required. |
| 5711 static const int kRequiredGCs = 5; | 5711 static const int kRequiredGCs = 5; |
| 5712 | 5712 |
| 5713 class GCThread : public i::Thread { | 5713 class GCThread : public i::Thread { |
| 5714 public: | 5714 public: |
| 5715 explicit GCThread(RegExpInterruptTest* test) | 5715 explicit GCThread(RegExpInterruptTest* test) |
| 5716 : test_(test) {} | 5716 : test_(test) {} |
| 5717 virtual void Run() { | 5717 virtual void Run() { |
| 5718 test_->CollectGarbage(); | 5718 test_->CollectGarbage(); |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 5734 } | 5734 } |
| 5735 gc_success_ = true; | 5735 gc_success_ = true; |
| 5736 } | 5736 } |
| 5737 | 5737 |
| 5738 void LongRunningRegExp() { | 5738 void LongRunningRegExp() { |
| 5739 block_->Signal(); // Enable garbage collection thread on next preemption. | 5739 block_->Signal(); // Enable garbage collection thread on next preemption. |
| 5740 int rounds = 0; | 5740 int rounds = 0; |
| 5741 while (gc_during_regexp_ < kRequiredGCs) { | 5741 while (gc_during_regexp_ < kRequiredGCs) { |
| 5742 int gc_before = gc_count_; | 5742 int gc_before = gc_count_; |
| 5743 { | 5743 { |
| 5744 // match 15-30 "a"'s against 14 and a "b". | 5744 // Match 15-30 "a"'s against 14 and a "b". |
| 5745 const char* c_source = | 5745 const char* c_source = |
|
Erik Corry
2009/03/20 12:45:42
It would be nice to test GC during execution of a
Lasse Reichstein
2009/03/20 13:26:11
Are they more likely to fail?
In any case, that wo
| |
| 5746 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" | 5746 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" |
| 5747 ".exec('aaaaaaaaaaaaaaab') === null"; | 5747 ".exec('aaaaaaaaaaaaaaab') === null"; |
| 5748 Local<String> source = String::New(c_source); | 5748 Local<String> source = String::New(c_source); |
| 5749 Local<Script> script = Script::Compile(source); | 5749 Local<Script> script = Script::Compile(source); |
| 5750 Local<Value> result = script->Run(); | 5750 Local<Value> result = script->Run(); |
| 5751 if (!result->BooleanValue()) { | 5751 if (!result->BooleanValue()) { |
| 5752 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. | 5752 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. |
| 5753 return; | 5753 return; |
| 5754 } | 5754 } |
| 5755 } | 5755 } |
| 5756 { | 5756 { |
| 5757 // match 15-30 "a"'s against 15 and a "b". | 5757 // Match 15-30 "a"'s against 15 and a "b". |
| 5758 const char* c_source = | 5758 const char* c_source = |
| 5759 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" | 5759 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" |
| 5760 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; | 5760 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; |
| 5761 Local<String> source = String::New(c_source); | 5761 Local<String> source = String::New(c_source); |
| 5762 Local<Script> script = Script::Compile(source); | 5762 Local<Script> script = Script::Compile(source); |
| 5763 Local<Value> result = script->Run(); | 5763 Local<Value> result = script->Run(); |
| 5764 if (!result->BooleanValue()) { | 5764 if (!result->BooleanValue()) { |
| 5765 gc_during_regexp_ = kRequiredGCs; | 5765 gc_during_regexp_ = kRequiredGCs; |
| 5766 return; | 5766 return; |
| 5767 } | 5767 } |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 5830 Local<v8::Object> clone = obj->Clone(); | 5830 Local<v8::Object> clone = obj->Clone(); |
| 5831 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); | 5831 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); |
| 5832 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); | 5832 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); |
| 5833 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); | 5833 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); |
| 5834 | 5834 |
| 5835 // Set a property on the clone, verify each object. | 5835 // Set a property on the clone, verify each object. |
| 5836 clone->Set(v8_str("beta"), v8::Integer::New(456)); | 5836 clone->Set(v8_str("beta"), v8::Integer::New(456)); |
| 5837 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); | 5837 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); |
| 5838 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); | 5838 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); |
| 5839 } | 5839 } |
| 5840 | |
| 5841 | |
| 5842 class RegExpStringModificationTest { | |
| 5843 public: | |
| 5844 RegExpStringModificationTest() | |
| 5845 : two_byte_content_(), | |
| 5846 block_(i::OS::CreateSemaphore(0)), | |
| 5847 morphs_(0), | |
| 5848 morphs_during_regexp_(0), | |
| 5849 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), | |
| 5850 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} | |
| 5851 ~RegExpStringModificationTest() { delete block_; } | |
| 5852 void RunTest() { | |
| 5853 regexp_success_ = false; | |
| 5854 morph_success_ = false; | |
| 5855 | |
| 5856 for (int i = 0; i < 14; i++) { | |
| 5857 two_byte_content_[i] = 'a'; | |
| 5858 } | |
| 5859 two_byte_content_[14] = 'b'; | |
| 5860 | |
| 5861 // Create the input string for the regexp - the one we are going to change | |
| 5862 // properties of. | |
| 5863 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_); | |
| 5864 | |
| 5865 // Inject the input as a global variable. | |
| 5866 i::Handle<i::String> input_name = | |
| 5867 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5)); | |
| 5868 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE); | |
| 5869 | |
| 5870 | |
| 5871 MorphThread morph_thread(this); | |
| 5872 morph_thread.Start(); | |
| 5873 v8::Locker::StartPreemption(1); | |
| 5874 LongRunningRegExp(); | |
| 5875 { | |
| 5876 v8::Unlocker unlock; | |
| 5877 morph_thread.Join(); | |
| 5878 } | |
| 5879 v8::Locker::StopPreemption(); | |
| 5880 CHECK(regexp_success_); | |
| 5881 CHECK(morph_success_); | |
| 5882 } | |
| 5883 private: | |
| 5884 | |
| 5885 class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { | |
| 5886 public: | |
| 5887 explicit AsciiVectorResource(i::Vector<const char> vector) | |
| 5888 : data_(vector) {} | |
| 5889 virtual ~AsciiVectorResource() {} | |
| 5890 virtual size_t length() const { return data_.length(); } | |
| 5891 virtual const char* data() const { return data_.start(); } | |
| 5892 private: | |
| 5893 i::Vector<const char> data_; | |
| 5894 }; | |
| 5895 class UC16VectorResource : public v8::String::ExternalStringResource { | |
| 5896 public: | |
| 5897 explicit UC16VectorResource(i::Vector<const i::uc16> vector) | |
| 5898 : data_(vector) {} | |
| 5899 virtual ~UC16VectorResource() {} | |
| 5900 virtual size_t length() const { return data_.length(); } | |
| 5901 virtual const i::uc16* data() const { return data_.start(); } | |
| 5902 private: | |
| 5903 i::Vector<const i::uc16> data_; | |
| 5904 }; | |
| 5905 // Number of string modifications required. | |
| 5906 static const int kRequiredModifications = 5; | |
| 5907 static const int kMaxModifications = 100; | |
| 5908 | |
| 5909 class MorphThread : public i::Thread { | |
| 5910 public: | |
| 5911 explicit MorphThread(RegExpStringModificationTest* test) | |
| 5912 : test_(test) {} | |
| 5913 virtual void Run() { | |
| 5914 test_->MorphString(); | |
| 5915 } | |
| 5916 private: | |
| 5917 RegExpStringModificationTest* test_; | |
| 5918 }; | |
| 5919 | |
| 5920 void MorphString() { | |
| 5921 block_->Wait(); | |
| 5922 while (morphs_during_regexp_ < kRequiredModifications && | |
| 5923 morphs_ < kMaxModifications) { | |
| 5924 { | |
| 5925 v8::Locker lock; | |
| 5926 // Swap string between ascii and two-byte representation. | |
| 5927 i::String* string = *input_; | |
| 5928 CHECK(i::StringShape(string).IsExternal()); | |
| 5929 if (i::StringShape(string).IsAsciiRepresentation()) { | |
| 5930 // Morph external string to be TwoByte string. | |
| 5931 i::ExternalAsciiString* ext_string = | |
| 5932 i::ExternalAsciiString::cast(string); | |
| 5933 i::ExternalTwoByteString* morphed = | |
| 5934 reinterpret_cast<i::ExternalTwoByteString*>(ext_string); | |
| 5935 morphed->map()->set_instance_type(i::SHORT_EXTERNAL_STRING_TYPE); | |
| 5936 morphed->set_resource(&uc16_resource_); | |
| 5937 } else { | |
| 5938 // Morph external string to be ASCII string. | |
| 5939 i::ExternalTwoByteString* ext_string = | |
| 5940 i::ExternalTwoByteString::cast(string); | |
| 5941 i::ExternalAsciiString* morphed = | |
| 5942 reinterpret_cast<i::ExternalAsciiString*>(ext_string); | |
| 5943 morphed->map()->set_instance_type( | |
| 5944 i::SHORT_EXTERNAL_ASCII_STRING_TYPE); | |
| 5945 morphed->set_resource(&ascii_resource_); | |
| 5946 } | |
| 5947 morphs_++; | |
| 5948 } | |
| 5949 i::OS::Sleep(1); | |
| 5950 } | |
| 5951 morph_success_ = true; | |
| 5952 } | |
| 5953 | |
| 5954 void LongRunningRegExp() { | |
| 5955 block_->Signal(); // Enable morphing thread on next preemption. | |
| 5956 while (morphs_during_regexp_ < kRequiredModifications && | |
| 5957 morphs_ < kMaxModifications) { | |
| 5958 int morphs_before = morphs_; | |
| 5959 { | |
| 5960 // Match 15-30 "a"'s against 14 and a "b". | |
|
Erik Corry
2009/03/20 12:45:42
14 "a"s
| |
| 5961 const char* c_source = | |
| 5962 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" | |
| 5963 ".exec(input) === null"; | |
| 5964 Local<String> source = String::New(c_source); | |
| 5965 Local<Script> script = Script::Compile(source); | |
| 5966 Local<Value> result = script->Run(); | |
| 5967 CHECK(result->IsTrue()); | |
| 5968 } | |
| 5969 int morphs_after = morphs_; | |
| 5970 morphs_during_regexp_ += morphs_after - morphs_before; | |
| 5971 } | |
| 5972 regexp_success_ = true; | |
| 5973 } | |
| 5974 | |
| 5975 i::uc16 two_byte_content_[15]; | |
| 5976 i::Semaphore* block_; | |
| 5977 int morphs_; | |
| 5978 int morphs_during_regexp_; | |
| 5979 bool regexp_success_; | |
| 5980 bool morph_success_; | |
| 5981 i::Handle<i::String> input_; | |
| 5982 AsciiVectorResource ascii_resource_; | |
| 5983 UC16VectorResource uc16_resource_; | |
| 5984 }; | |
| 5985 | |
| 5986 | |
| 5987 // Test that a regular expression execution can be interrupted and | |
| 5988 // the string changed without failing. | |
| 5989 TEST(RegExpStringModification) { | |
| 5990 v8::Locker lock; | |
| 5991 v8::V8::Initialize(); | |
| 5992 v8::HandleScope scope; | |
| 5993 Local<Context> local_env; | |
| 5994 { | |
| 5995 LocalContext env; | |
| 5996 local_env = env.local(); | |
| 5997 } | |
| 5998 | |
| 5999 // Local context should still be live. | |
| 6000 CHECK(!local_env.IsEmpty()); | |
| 6001 local_env->Enter(); | |
| 6002 | |
| 6003 // Should complete without problems. | |
| 6004 RegExpStringModificationTest().RunTest(); | |
| 6005 | |
| 6006 local_env->Exit(); | |
| 6007 } | |
| OLD | NEW |