Index: test/cctest/test-strings.cc |
=================================================================== |
--- test/cctest/test-strings.cc (revision 1850) |
+++ test/cctest/test-strings.cc (working copy) |
@@ -9,6 +9,7 @@ |
#include "v8.h" |
+#include "api.h" |
#include "factory.h" |
#include "cctest.h" |
#include "zone-inl.h" |
@@ -391,16 +392,27 @@ |
class TwoByteResource: public v8::String::ExternalStringResource { |
public: |
- explicit TwoByteResource(const uint16_t* data, size_t length) |
- : data_(data), length_(length) { } |
- virtual ~TwoByteResource() { } |
+ TwoByteResource(const uint16_t* data, size_t length, bool* destructed) |
+ : data_(data), length_(length), destructed_(destructed) { |
+ if (destructed_ != NULL) { |
+ *destructed_ = false; |
+ } |
+ } |
+ virtual ~TwoByteResource() { |
+ if (destructed_ != NULL) { |
+ CHECK(!*destructed_); |
+ *destructed_ = true; |
+ } |
+ } |
+ |
const uint16_t* data() const { return data_; } |
size_t length() const { return length_; } |
private: |
const uint16_t* data_; |
size_t length_; |
+ bool* destructed_; |
}; |
@@ -420,7 +432,8 @@ |
// Allocate an external string resource and external string. |
TwoByteResource* resource = new TwoByteResource(two_byte_data, |
- two_byte_length); |
+ two_byte_length, |
+ NULL); |
Handle<String> string = Factory::NewExternalStringFromTwoByte(resource); |
Vector<const char> one_byte_vec = CStrVector(one_byte_data); |
Handle<String> compare = Factory::NewStringFromAscii(one_byte_vec); |
@@ -444,3 +457,87 @@ |
CHECK_EQ(false, string->Equals(Heap::empty_string())); |
#endif // !defined(DEBUG) |
} |
+ |
+ |
+// Regression test case for http://crbug.com/9746. The problem was |
+// that when we marked objects reachable only through weak pointers, |
+// we ended up keeping a sliced symbol alive, even though we already |
+// invoked the weak callback on the underlying external string thus |
+// deleting its resource. |
+TEST(Regress9746) { |
+ InitializeVM(); |
+ |
+ // Setup lengths that guarantee we'll get slices instead of simple |
+ // flat strings. |
+ static const int kFullStringLength = String::kMinNonFlatLength * 2; |
+ static const int kSliceStringLength = String::kMinNonFlatLength + 1; |
+ |
+ uint16_t* source = new uint16_t[kFullStringLength]; |
+ for (int i = 0; i < kFullStringLength; i++) source[i] = '1'; |
+ char* key = new char[kSliceStringLength]; |
+ for (int i = 0; i < kSliceStringLength; i++) key[i] = '1'; |
+ |
+ // Allocate an external string resource that keeps track of when it |
+ // is destructed. |
+ bool resource_destructed = false; |
+ TwoByteResource* resource = |
+ new TwoByteResource(source, kFullStringLength, &resource_destructed); |
+ |
+ { |
+ v8::HandleScope scope; |
+ |
+ // Allocate an external string resource and external string. We |
+ // have to go through the API to get the weak handle and the |
+ // automatic destruction going. |
+ Handle<String> string = |
+ v8::Utils::OpenHandle(*v8::String::NewExternal(resource)); |
+ |
+ // Create a slice of the external string. |
+ Handle<String> slice = |
+ Factory::NewStringSlice(string, 0, kSliceStringLength); |
+ CHECK_EQ(kSliceStringLength, slice->length()); |
+ CHECK(StringShape(*slice).IsSliced()); |
+ |
+ // Make sure the slice ends up in old space so we can morph it |
+ // into a symbol. |
+ while (Heap::InNewSpace(*slice)) { |
Kevin Millikin (Chromium)
2009/05/05 08:13:00
This will probably terminate :)
|
+ Heap::PerformScavenge(); |
+ } |
+ |
+ // Force the slice into the symbol table. |
+ slice = Factory::SymbolFromString(slice); |
+ CHECK(slice->IsSymbol()); |
+ CHECK(StringShape(*slice).IsSliced()); |
+ |
+ Handle<String> buffer(Handle<SlicedString>::cast(slice)->buffer()); |
+ CHECK(StringShape(*buffer).IsExternal()); |
+ CHECK(buffer->IsTwoByteRepresentation()); |
+ |
+ // Finally, base a script on the slice of the external string and |
+ // get its wrapper. This allocated yet another weak handle that |
Kevin Millikin (Chromium)
2009/05/05 08:13:00
"allocated" => "allocates"
|
+ // indirectly refers to the external string. |
+ Handle<Script> script = Factory::NewScript(slice); |
+ Handle<JSObject> wrapper = GetScriptWrapper(script); |
+ } |
+ |
+ // When we collect all garbage, we cannot get rid of the sliced |
+ // symbol entry in the symbol table because it is used by the script |
+ // kept alive by the weak wrapper. Make sure we don't destruct the |
+ // external string. |
+ Heap::CollectAllGarbage(); |
+ CHECK(!resource_destructed); |
+ |
+ // Make sure the sliced symbol is still in the table. |
+ v8::HandleScope scope; |
+ Vector<const char> vector(key, kSliceStringLength); |
+ Handle<String> symbol = Factory::LookupSymbol(vector); |
+ CHECK(StringShape(*symbol).IsSliced()); |
+ |
+ // Make sure the buffer is still a two-byte external string. |
+ Handle<String> buffer(Handle<SlicedString>::cast(symbol)->buffer()); |
+ CHECK(StringShape(*buffer).IsExternal()); |
+ CHECK(buffer->IsTwoByteRepresentation()); |
+ |
+ delete[] source; |
+ delete[] key; |
+} |