Push version 1.2.2 to trunk.
Fixed bug in array sorting for sparse arrays (issue 326).
Added support for adding a soname when building a shared library on Linux (issue 151).
Fixed bug caused by morphing internal ASCII strings to external two-byte strings. Slices over ASCII strings have to forward ASCII checks to the underlying buffer string.
Allowed API call-as-function handlers to be called as constructors.
Fixed a crash bug where an external string was disposed but a slice of the external string survived as a symbol.
git-svn-id: http://v8.googlecode.com/svn/trunk@1853 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/test/cctest/SConscript b/test/cctest/SConscript
index f66f72b..eb94fc5 100644
--- a/test/cctest/SConscript
+++ b/test/cctest/SConscript
@@ -54,7 +54,8 @@
'test-spaces.cc',
'test-strings.cc',
'test-threads.cc',
- 'test-utils.cc'
+ 'test-utils.cc',
+ 'test-version.cc'
],
'arch:arm': ['test-assembler-arm.cc', 'test-disasm-arm.cc'],
'arch:ia32': [
diff --git a/test/cctest/cctest.cc b/test/cctest/cctest.cc
index 2807c8b..82a33e6 100644
--- a/test/cctest/cctest.cc
+++ b/test/cctest/cctest.cc
@@ -26,9 +26,6 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <v8.h>
-#include <cstdlib>
-#include <cstring>
-#include <cstdio>
#include "cctest.h"
#include "debug.h"
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 9a3aead..a59b1d4 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -27,9 +27,6 @@
#include <stdlib.h>
-#include <map>
-#include <string>
-
#include "v8.h"
#include "api.h"
@@ -423,7 +420,7 @@
public:
static int dispose_count;
- explicit TestAsciiResource(char* data)
+ explicit TestAsciiResource(const char* data)
: data_(data),
length_(strlen(data)) { }
@@ -440,7 +437,7 @@
return length_;
}
private:
- char* data_;
+ const char* data_;
size_t length_;
};
@@ -4659,11 +4656,6 @@
value = Script::Compile(v8_str(call_17))->Run();
CHECK(!try_catch.HasCaught());
CHECK_EQ(17, value->Int32Value());
-
- // Try something that will cause an exception: Call the object as a
- // constructor. This should be the last test.
- value = Script::Compile(v8_str("new obj(42)"))->Run();
- CHECK(try_catch.HasCaught());
}
@@ -6156,6 +6148,117 @@
}
+class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
+ public:
+ explicit AsciiVectorResource(i::Vector<const char> vector)
+ : data_(vector) {}
+ virtual ~AsciiVectorResource() {}
+ virtual size_t length() const { return data_.length(); }
+ virtual const char* data() const { return data_.start(); }
+ private:
+ i::Vector<const char> data_;
+};
+
+
+class UC16VectorResource : public v8::String::ExternalStringResource {
+ public:
+ explicit UC16VectorResource(i::Vector<const i::uc16> vector)
+ : data_(vector) {}
+ virtual ~UC16VectorResource() {}
+ virtual size_t length() const { return data_.length(); }
+ virtual const i::uc16* data() const { return data_.start(); }
+ private:
+ i::Vector<const i::uc16> data_;
+};
+
+
+static void MorphAString(i::String* string,
+ AsciiVectorResource* ascii_resource,
+ UC16VectorResource* uc16_resource) {
+ CHECK(i::StringShape(string).IsExternal());
+ if (string->IsAsciiRepresentation()) {
+ // Check old map is not symbol or long.
+ CHECK(string->map() == i::Heap::short_external_ascii_string_map() ||
+ string->map() == i::Heap::medium_external_ascii_string_map());
+ // Morph external string to be TwoByte string.
+ if (string->length() <= i::String::kMaxShortStringSize) {
+ string->set_map(i::Heap::short_external_string_map());
+ } else {
+ string->set_map(i::Heap::medium_external_string_map());
+ }
+ i::ExternalTwoByteString* morphed =
+ i::ExternalTwoByteString::cast(string);
+ morphed->set_resource(uc16_resource);
+ } else {
+ // Check old map is not symbol or long.
+ CHECK(string->map() == i::Heap::short_external_string_map() ||
+ string->map() == i::Heap::medium_external_string_map());
+ // Morph external string to be ASCII string.
+ if (string->length() <= i::String::kMaxShortStringSize) {
+ string->set_map(i::Heap::short_external_ascii_string_map());
+ } else {
+ string->set_map(i::Heap::medium_external_ascii_string_map());
+ }
+ i::ExternalAsciiString* morphed =
+ i::ExternalAsciiString::cast(string);
+ morphed->set_resource(ascii_resource);
+ }
+}
+
+
+// Test that we can still flatten a string if the components it is built up
+// from have been turned into 16 bit strings in the mean time.
+THREADED_TEST(MorphCompositeStringTest) {
+ const char* c_string = "Now is the time for all good men"
+ " to come to the aid of the party";
+ uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
+ {
+ v8::HandleScope scope;
+ LocalContext env;
+ AsciiVectorResource ascii_resource(
+ i::Vector<const char>(c_string, strlen(c_string)));
+ UC16VectorResource uc16_resource(
+ i::Vector<const uint16_t>(two_byte_string, strlen(c_string)));
+
+ Local<String> lhs(v8::Utils::ToLocal(
+ i::Factory::NewExternalStringFromAscii(&ascii_resource)));
+ Local<String> rhs(v8::Utils::ToLocal(
+ i::Factory::NewExternalStringFromAscii(&ascii_resource)));
+
+ env->Global()->Set(v8_str("lhs"), lhs);
+ env->Global()->Set(v8_str("rhs"), rhs);
+
+ CompileRun(
+ "var cons = lhs + rhs;"
+ "var slice = lhs.substring(1, lhs.length - 1);"
+ "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
+
+ MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
+ MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
+
+ // Now do some stuff to make sure the strings are flattened, etc.
+ CompileRun(
+ "/[^a-z]/.test(cons);"
+ "/[^a-z]/.test(slice);"
+ "/[^a-z]/.test(slice_on_cons);");
+ const char* expected_cons =
+ "Now is the time for all good men to come to the aid of the party"
+ "Now is the time for all good men to come to the aid of the party";
+ const char* expected_slice =
+ "ow is the time for all good men to come to the aid of the part";
+ const char* expected_slice_on_cons =
+ "ow is the time for all good men to come to the aid of the party"
+ "Now is the time for all good men to come to the aid of the part";
+ CHECK_EQ(String::New(expected_cons),
+ env->Global()->Get(v8_str("cons")));
+ CHECK_EQ(String::New(expected_slice),
+ env->Global()->Get(v8_str("slice")));
+ CHECK_EQ(String::New(expected_slice_on_cons),
+ env->Global()->Get(v8_str("slice_on_cons")));
+ }
+}
+
+
class RegExpStringModificationTest {
public:
RegExpStringModificationTest()
@@ -6200,26 +6303,6 @@
}
private:
- class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
- public:
- explicit AsciiVectorResource(i::Vector<const char> vector)
- : data_(vector) {}
- virtual ~AsciiVectorResource() {}
- virtual size_t length() const { return data_.length(); }
- virtual const char* data() const { return data_.start(); }
- private:
- i::Vector<const char> data_;
- };
- class UC16VectorResource : public v8::String::ExternalStringResource {
- public:
- explicit UC16VectorResource(i::Vector<const i::uc16> vector)
- : data_(vector) {}
- virtual ~UC16VectorResource() {}
- virtual size_t length() const { return data_.length(); }
- virtual const i::uc16* data() const { return data_.start(); }
- private:
- i::Vector<const i::uc16> data_;
- };
// Number of string modifications required.
static const int kRequiredModifications = 5;
static const int kMaxModifications = 100;
@@ -6243,25 +6326,7 @@
v8::Locker lock;
// Swap string between ascii and two-byte representation.
i::String* string = *input_;
- CHECK(i::StringShape(string).IsExternal());
- if (i::StringShape(string).IsAsciiRepresentation()) {
- // Morph external string to be TwoByte string.
- i::ExternalAsciiString* ext_string =
- i::ExternalAsciiString::cast(string);
- i::ExternalTwoByteString* morphed =
- reinterpret_cast<i::ExternalTwoByteString*>(ext_string);
- morphed->map()->set_instance_type(i::SHORT_EXTERNAL_STRING_TYPE);
- morphed->set_resource(&uc16_resource_);
- } else {
- // Morph external string to be ASCII string.
- i::ExternalTwoByteString* ext_string =
- i::ExternalTwoByteString::cast(string);
- i::ExternalAsciiString* morphed =
- reinterpret_cast<i::ExternalAsciiString*>(ext_string);
- morphed->map()->set_instance_type(
- i::SHORT_EXTERNAL_ASCII_STRING_TYPE);
- morphed->set_resource(&ascii_resource_);
- }
+ MorphAString(string, &ascii_resource_, &uc16_resource_);
morphs_++;
}
i::OS::Sleep(1);
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index ca2d49e..93286eb 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -3436,6 +3436,7 @@
// Signals when a break is reported.
message_queue_barriers.semaphore_2->Signal();
}
+
// Allow message handler to block on a semaphore, to test queueing of
// messages while blocked.
message_queue_barriers.semaphore_1->Wait();
@@ -3474,6 +3475,7 @@
/* Interleaved sequence of actions by the two threads:*/
// Main thread compiles and runs source_1
+ message_queue_barriers.semaphore_1->Signal();
message_queue_barriers.barrier_1.Wait();
// Post 6 commands, filling the command queue and making it expand.
// These calls return immediately, but the commands stay on the queue
@@ -3487,22 +3489,39 @@
v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_3, buffer_2));
message_queue_barriers.barrier_2.Wait();
// Main thread compiles and runs source_2.
- // Queued commands are executed at the start of compilation of source_2.
- message_queue_barriers.barrier_3.Wait();
- // Free the message handler to process all the messages from the queue.
- for (int i = 0; i < 20 ; ++i) {
+ // Queued commands are executed at the start of compilation of source_2(
+ // beforeCompile event).
+ // Free the message handler to process all the messages from the queue. 7
+ // messages are expected: 2 afterCompile events and 5 responses.
+ // All the commands added so far will fail to execute as long as call stack
+ // is empty on beforeCompile event.
+ for (int i = 0; i < 6 ; ++i) {
message_queue_barriers.semaphore_1->Signal();
}
+ message_queue_barriers.barrier_3.Wait();
// Main thread compiles and runs source_3.
+ // Don't stop in the afterCompile handler.
+ message_queue_barriers.semaphore_1->Signal();
// source_3 includes a debugger statement, which causes a break event.
// Wait on break event from hitting "debugger" statement
message_queue_barriers.semaphore_2->Wait();
// These should execute after the "debugger" statement in source_2
+ v8::Debug::SendCommand(buffer_1, AsciiToUtf16(command_1, buffer_1));
+ v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_2, buffer_2));
+ v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_3, buffer_2));
v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_single_step, buffer_2));
+ // Run after 2 break events, 4 responses.
+ for (int i = 0; i < 6 ; ++i) {
+ message_queue_barriers.semaphore_1->Signal();
+ }
// Wait on break event after a single step executes.
message_queue_barriers.semaphore_2->Wait();
v8::Debug::SendCommand(buffer_1, AsciiToUtf16(command_2, buffer_1));
v8::Debug::SendCommand(buffer_2, AsciiToUtf16(command_continue, buffer_2));
+ // Run after 2 responses.
+ for (int i = 0; i < 2 ; ++i) {
+ message_queue_barriers.semaphore_1->Signal();
+ }
// Main thread continues running source_3 to end, waits for this thread.
}
@@ -3593,10 +3612,8 @@
static int handled_client_data_instances_count = 0;
static void MessageHandlerCountingClientData(
- const uint16_t* message,
- int length,
- v8::Debug::ClientData* client_data) {
- if (client_data) {
+ const v8::Debug::Message& message) {
+ if (message.GetClientData() != NULL) {
handled_client_data_instances_count++;
}
}
@@ -3609,8 +3626,8 @@
DebugLocalContext env;
TestClientData::ResetCounters();
handled_client_data_instances_count = 0;
- v8::Debug::SetMessageHandler(MessageHandlerCountingClientData);
- const char* source_1 = "a = 3; b = 4; c = new Object(); c.d = 5; debugger;";
+ v8::Debug::SetMessageHandler2(MessageHandlerCountingClientData);
+ const char* source_1 = "a = 3; b = 4; c = new Object(); c.d = 5;";
const int kBufferSize = 1000;
uint16_t buffer[kBufferSize];
const char* command_1 =
@@ -3635,8 +3652,9 @@
new TestClientData());
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer),
new TestClientData());
- v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
+ // All the messages will be processed on beforeCompile event.
CompileRun(source_1);
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
CHECK_EQ(3, TestClientData::constructor_call_counter);
CHECK_EQ(TestClientData::constructor_call_counter,
handled_client_data_instances_count);
@@ -3671,10 +3689,10 @@
}
-static void ThreadedMessageHandler(const uint16_t* message, int length,
- v8::Debug::ClientData* client_data) {
+static void ThreadedMessageHandler(const v8::Debug::Message& message) {
static char print_buffer[1000];
- Utf16ToAscii(message, length, print_buffer);
+ v8::String::Value json(message.GetJSON());
+ Utf16ToAscii(*json, json.length(), print_buffer);
if (IsBreakEventMessage(print_buffer)) {
threaded_debugging_barriers.barrier_2.Wait();
}
@@ -3705,7 +3723,7 @@
v8::HandleScope scope;
DebugLocalContext env;
- v8::Debug::SetMessageHandler(&ThreadedMessageHandler);
+ v8::Debug::SetMessageHandler2(&ThreadedMessageHandler);
v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
global_template->Set(v8::String::New("ThreadedAtBarrier1"),
v8::FunctionTemplate::New(ThreadedAtBarrier1));
@@ -3768,11 +3786,10 @@
Barriers* breakpoints_barriers;
-static void BreakpointsMessageHandler(const uint16_t* message,
- int length,
- v8::Debug::ClientData* client_data) {
+static void BreakpointsMessageHandler(const v8::Debug::Message& message) {
static char print_buffer[1000];
- Utf16ToAscii(message, length, print_buffer);
+ v8::String::Value json(message.GetJSON());
+ Utf16ToAscii(*json, json.length(), print_buffer);
printf("%s\n", print_buffer);
fflush(stdout);
@@ -3806,7 +3823,7 @@
v8::HandleScope scope;
DebugLocalContext env;
- v8::Debug::SetMessageHandler(&BreakpointsMessageHandler);
+ v8::Debug::SetMessageHandler2(&BreakpointsMessageHandler);
CompileRun(source_1);
breakpoints_barriers->barrier_1.Wait();
@@ -3915,14 +3932,12 @@
}
-static void DummyMessageHandler(const uint16_t* message,
- int length,
- v8::Debug::ClientData* client_data) {
+static void DummyMessageHandler(const v8::Debug::Message& message) {
}
TEST(SetMessageHandlerOnUninitializedVM) {
- v8::Debug::SetMessageHandler(DummyMessageHandler);
+ v8::Debug::SetMessageHandler2(DummyMessageHandler);
}
@@ -4142,9 +4157,7 @@
// Debugger message handler which counts the number of times it is called.
static int message_handler_hit_count = 0;
-static void MessageHandlerHitCount(const uint16_t* message,
- int length,
- v8::Debug::ClientData* client_data) {
+static void MessageHandlerHitCount(const v8::Debug::Message& message) {
message_handler_hit_count++;
const int kBufferSize = 1000;
@@ -4167,7 +4180,7 @@
CheckDebuggerUnloaded();
// Set a debug message handler.
- v8::Debug::SetMessageHandler(MessageHandlerHitCount);
+ v8::Debug::SetMessageHandler2(MessageHandlerHitCount);
// Run code to throw a unhandled exception. This should end up in the message
// handler.
@@ -4193,9 +4206,7 @@
// Debugger message handler which clears the message handler while active.
static void MessageHandlerClearingMessageHandler(
- const uint16_t* message,
- int length,
- v8::Debug::ClientData* client_data) {
+ const v8::Debug::Message& message) {
message_handler_hit_count++;
// Clear debug message handler.
@@ -4212,7 +4223,7 @@
CheckDebuggerUnloaded();
// Set a debug message handler.
- v8::Debug::SetMessageHandler(MessageHandlerClearingMessageHandler);
+ v8::Debug::SetMessageHandler2(MessageHandlerClearingMessageHandler);
// Run code to throw a unhandled exception. This should end up in the message
// handler.
@@ -4242,11 +4253,10 @@
Barriers* host_dispatch_barriers;
-static void HostDispatchMessageHandler(const uint16_t* message,
- int length,
- v8::Debug::ClientData* client_data) {
+static void HostDispatchMessageHandler(const v8::Debug::Message& message) {
static char print_buffer[1000];
- Utf16ToAscii(message, length, print_buffer);
+ v8::String::Value json(message.GetJSON());
+ Utf16ToAscii(*json, json.length(), print_buffer);
printf("%s\n", print_buffer);
fflush(stdout);
}
@@ -4273,7 +4283,7 @@
DebugLocalContext env;
// Setup message and host dispatch handlers.
- v8::Debug::SetMessageHandler(HostDispatchMessageHandler);
+ v8::Debug::SetMessageHandler2(HostDispatchMessageHandler);
v8::Debug::SetHostDispatchHandler(HostDispatchDispatchHandler, 10 /* ms */);
CompileRun(source_1);
diff --git a/test/cctest/test-regexp.cc b/test/cctest/test-regexp.cc
index f2f66fb..ac75999 100644
--- a/test/cctest/test-regexp.cc
+++ b/test/cctest/test-regexp.cc
@@ -1,4 +1,4 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+/// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -27,7 +27,6 @@
#include <stdlib.h>
-#include <set>
#include "v8.h"
@@ -39,9 +38,13 @@
#include "jsregexp-inl.h"
#include "regexp-macro-assembler.h"
#include "regexp-macro-assembler-irregexp.h"
-#ifdef ARM
+#ifdef V8_ARCH_ARM
#include "arm/regexp-macro-assembler-arm.h"
-#else // IA32
+#endif
+#ifdef V8_ARCH_X64
+// No X64-implementation yet.
+#endif
+#ifdef V8_ARCH_IA32
#include "ia32/macro-assembler-ia32.h"
#include "ia32/regexp-macro-assembler-ia32.h"
#endif
@@ -499,24 +502,25 @@
const int TestConfig::kNoValue = 0;
-static int PseudoRandom(int i, int j) {
+static unsigned PseudoRandom(int i, int j) {
return ~(~((i * 781) ^ (j * 329)));
}
TEST(SplayTreeSimple) {
- static const int kLimit = 1000;
+ static const unsigned kLimit = 1000;
ZoneScope zone_scope(DELETE_ON_EXIT);
ZoneSplayTree<TestConfig> tree;
- std::set<int> seen;
+ bool seen[kLimit];
+ for (unsigned i = 0; i < kLimit; i++) seen[i] = false;
#define CHECK_MAPS_EQUAL() do { \
- for (int k = 0; k < kLimit; k++) \
- CHECK_EQ(seen.find(k) != seen.end(), tree.Find(k, &loc)); \
+ for (unsigned k = 0; k < kLimit; k++) \
+ CHECK_EQ(seen[k], tree.Find(k, &loc)); \
} while (false)
for (int i = 0; i < 50; i++) {
for (int j = 0; j < 50; j++) {
- int next = PseudoRandom(i, j) % kLimit;
- if (seen.find(next) != seen.end()) {
+ unsigned next = PseudoRandom(i, j) % kLimit;
+ if (seen[next]) {
// We've already seen this one. Check the value and remove
// it.
ZoneSplayTree<TestConfig>::Locator loc;
@@ -524,7 +528,7 @@
CHECK_EQ(next, loc.key());
CHECK_EQ(3 * next, loc.value());
tree.Remove(next);
- seen.erase(next);
+ seen[next] = false;
CHECK_MAPS_EQUAL();
} else {
// Check that it wasn't there already and then add it.
@@ -533,26 +537,22 @@
CHECK(tree.Insert(next, &loc));
CHECK_EQ(next, loc.key());
loc.set_value(3 * next);
- seen.insert(next);
+ seen[next] = true;
CHECK_MAPS_EQUAL();
}
int val = PseudoRandom(j, i) % kLimit;
- for (int k = val; k >= 0; k--) {
- if (seen.find(val) != seen.end()) {
- ZoneSplayTree<TestConfig>::Locator loc;
- CHECK(tree.FindGreatestLessThan(val, &loc));
- CHECK_EQ(loc.key(), val);
- break;
- }
+ if (seen[val]) {
+ ZoneSplayTree<TestConfig>::Locator loc;
+ CHECK(tree.FindGreatestLessThan(val, &loc));
+ CHECK_EQ(loc.key(), val);
+ break;
}
val = PseudoRandom(i + j, i - j) % kLimit;
- for (int k = val; k < kLimit; k++) {
- if (seen.find(val) != seen.end()) {
- ZoneSplayTree<TestConfig>::Locator loc;
- CHECK(tree.FindLeastGreaterThan(val, &loc));
- CHECK_EQ(loc.key(), val);
- break;
- }
+ if (seen[val]) {
+ ZoneSplayTree<TestConfig>::Locator loc;
+ CHECK(tree.FindLeastGreaterThan(val, &loc));
+ CHECK_EQ(loc.key(), val);
+ break;
}
}
}
@@ -661,7 +661,7 @@
}
-#ifndef ARM // IA32 only tests.
+#ifdef V8_ARCH_IA32 // IA32 only tests.
class ContextInitializer {
public:
diff --git a/test/cctest/test-serialize.cc b/test/cctest/test-serialize.cc
index 56727dc..36f051f 100644
--- a/test/cctest/test-serialize.cc
+++ b/test/cctest/test-serialize.cc
@@ -26,8 +26,6 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <signal.h>
-#include <map>
-#include <string>
#include "sys/stat.h"
#include "v8.h"
@@ -42,20 +40,40 @@
using namespace v8::internal;
-static int local_counters[256];
-static int counter_count = 0;
-static std::map<std::string, int> counter_table;
+static const unsigned kCounters = 256;
+static int local_counters[kCounters];
+static const char* local_counter_names[kCounters];
+
+
+static unsigned CounterHash(const char* s) {
+ unsigned hash = 0;
+ while (*++s) {
+ hash |= hash << 5;
+ hash += *s;
+ }
+ return hash;
+}
// Callback receiver to track counters in test.
static int* counter_function(const char* name) {
- std::string counter(name);
- if (counter_table.find(counter) == counter_table.end()) {
- local_counters[counter_count] = 0;
- counter_table[counter] = counter_count++;
+ unsigned hash = CounterHash(name) % kCounters;
+ unsigned original_hash = hash;
+ USE(original_hash);
+ while (true) {
+ if (local_counter_names[hash] == name) {
+ return &local_counters[hash];
+ }
+ if (local_counter_names[hash] == 0) {
+ local_counter_names[hash] = name;
+ return &local_counters[hash];
+ }
+ if (strcmp(local_counter_names[hash], name) == 0) {
+ return &local_counters[hash];
+ }
+ hash = (hash + 1) % kCounters;
+ ASSERT(hash != original_hash); // Hash table has been filled up.
}
-
- return &local_counters[counter_table[counter]];
}
diff --git a/test/cctest/test-strings.cc b/test/cctest/test-strings.cc
index 1788cd2..6f42639 100644
--- a/test/cctest/test-strings.cc
+++ b/test/cctest/test-strings.cc
@@ -9,6 +9,7 @@
#include "v8.h"
+#include "api.h"
#include "factory.h"
#include "cctest.h"
#include "zone-inl.h"
@@ -391,9 +392,19 @@
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_; }
@@ -401,6 +412,7 @@
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)) {
+ 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
+ // 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;
+}
diff --git a/test/cctest/test-version.cc b/test/cctest/test-version.cc
new file mode 100644
index 0000000..0b93fbf
--- /dev/null
+++ b/test/cctest/test-version.cc
@@ -0,0 +1,88 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#include "version.h"
+#include "cctest.h"
+
+using namespace v8::internal;
+
+
+namespace v8 { namespace internal {
+
+void SetVersion(int major, int minor, int build, int patch,
+ bool candidate, const char* soname) {
+ Version::major_ = major;
+ Version::minor_ = minor;
+ Version::build_ = build;
+ Version::patch_ = patch;
+ Version::candidate_ = candidate;
+ Version::soname_ = soname;
+}
+
+} } // namespace v8::internal
+
+
+static void CheckVersion(int major, int minor, int build,
+ int patch, bool candidate,
+ const char* expected_version_string,
+ const char* expected_generic_soname) {
+ static v8::internal::EmbeddedVector<char, 128> version_str;
+ static v8::internal::EmbeddedVector<char, 128> soname_str;
+
+ // Test version without specific SONAME.
+ SetVersion(major, minor, build, patch, candidate, "");
+ Version::GetString(version_str);
+ CHECK_EQ(expected_version_string, version_str.start());
+ Version::GetSONAME(soname_str);
+ CHECK_EQ(expected_generic_soname, soname_str.start());
+
+ // Test version with specific SONAME.
+ const char* soname = "libv8.so.1";
+ SetVersion(major, minor, build, patch, candidate, soname);
+ Version::GetString(version_str);
+ CHECK_EQ(expected_version_string, version_str.start());
+ Version::GetSONAME(soname_str);
+ CHECK_EQ(soname, soname_str.start());
+}
+
+
+TEST(VersionString) {
+ CheckVersion(0, 0, 0, 0, false, "0.0.0", "libv8-0.0.0.so");
+ CheckVersion(0, 0, 0, 0, true,
+ "0.0.0 (candidate)", "libv8-0.0.0-candidate.so");
+ CheckVersion(1, 0, 0, 0, false, "1.0.0", "libv8-1.0.0.so");
+ CheckVersion(1, 0, 0, 0, true,
+ "1.0.0 (candidate)", "libv8-1.0.0-candidate.so");
+ CheckVersion(1, 0, 0, 1, false, "1.0.0.1", "libv8-1.0.0.1.so");
+ CheckVersion(1, 0, 0, 1, true,
+ "1.0.0.1 (candidate)", "libv8-1.0.0.1-candidate.so");
+ CheckVersion(2, 5, 10, 7, false, "2.5.10.7", "libv8-2.5.10.7.so");
+ CheckVersion(2, 5, 10, 7, true,
+ "2.5.10.7 (candidate)", "libv8-2.5.10.7-candidate.so");
+}