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");
+}