Version 2.2.15

Add an API to control the disposal of external string resources.

Add missing initialization of a couple of variables which makes some compilers complaint when compiling with -Werror.

Improve performance on all platforms.

git-svn-id: http://v8.googlecode.com/svn/trunk@4809 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index a33eb94..b520e56 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -27,6 +27,8 @@
 
 #include <limits.h>
 
+#define USE_NEW_QUERY_CALLBACKS
+
 #include "v8.h"
 
 #include "api.h"
@@ -610,6 +612,71 @@
 }
 
 
+static int dispose_count = 0;
+static void DisposeExternalStringCount(
+    String::ExternalStringResourceBase* resource) {
+  dispose_count++;
+}
+
+
+static void DisposeExternalStringDeleteAndCount(
+    String::ExternalStringResourceBase* resource) {
+  delete resource;
+  dispose_count++;
+}
+
+
+TEST(ExternalStringWithResourceDisposeCallback) {
+  const char* c_source = "1 + 2 * 3";
+
+  // Set an external string collected callback which does not delete the object.
+  v8::V8::SetExternalStringDiposeCallback(DisposeExternalStringCount);
+
+  // Use a stack allocated external string resource allocated object.
+  dispose_count = 0;
+  TestAsciiResource::dispose_count = 0;
+  TestAsciiResource res_stack(i::StrDup(c_source));
+  {
+    v8::HandleScope scope;
+    LocalContext env;
+    Local<String> source =  String::NewExternal(&res_stack);
+    Local<Script> script = Script::Compile(source);
+    Local<Value> value = script->Run();
+    CHECK(value->IsNumber());
+    CHECK_EQ(7, value->Int32Value());
+    v8::internal::Heap::CollectAllGarbage(false);
+    CHECK_EQ(0, TestAsciiResource::dispose_count);
+  }
+  v8::internal::CompilationCache::Clear();
+  v8::internal::Heap::CollectAllGarbage(false);
+  CHECK_EQ(1, dispose_count);
+  CHECK_EQ(0, TestAsciiResource::dispose_count);
+
+  // Set an external string collected callback which does delete the object.
+  v8::V8::SetExternalStringDiposeCallback(DisposeExternalStringDeleteAndCount);
+
+  // Use a heap allocated external string resource allocated object.
+  dispose_count = 0;
+  TestAsciiResource::dispose_count = 0;
+  TestAsciiResource* res_heap = new TestAsciiResource(i::StrDup(c_source));
+  {
+    v8::HandleScope scope;
+    LocalContext env;
+    Local<String> source =  String::NewExternal(res_heap);
+    Local<Script> script = Script::Compile(source);
+    Local<Value> value = script->Run();
+    CHECK(value->IsNumber());
+    CHECK_EQ(7, value->Int32Value());
+    v8::internal::Heap::CollectAllGarbage(false);
+    CHECK_EQ(0, TestAsciiResource::dispose_count);
+  }
+  v8::internal::CompilationCache::Clear();
+  v8::internal::Heap::CollectAllGarbage(false);
+  CHECK_EQ(1, dispose_count);
+  CHECK_EQ(1, TestAsciiResource::dispose_count);
+}
+
+
 THREADED_TEST(StringConcat) {
   {
     v8::HandleScope scope;
@@ -1120,11 +1187,11 @@
 }
 
 
-v8::Handle<v8::Boolean> CheckThisNamedPropertyQuery(Local<String> property,
+v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property,
                                                     const AccessorInfo& info) {
   ApiTestFuzzer::Fuzz();
   CHECK(info.This()->Equals(bottom));
-  return v8::Handle<v8::Boolean>();
+  return v8::Handle<v8::Integer>();
 }
 
 
@@ -1221,13 +1288,13 @@
 }
 
 
-static v8::Handle<v8::Boolean> PrePropertyHandlerHas(Local<String> key,
-                                                     const AccessorInfo&) {
+static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key,
+                                                       const AccessorInfo&) {
   if (v8_str("pre")->Equals(key)) {
-    return v8::True();
+    return v8::Integer::New(v8::None);
   }
 
-  return v8::Handle<v8::Boolean>();  // do not intercept the call
+  return v8::Handle<v8::Integer>();  // do not intercept the call
 }
 
 
@@ -1236,7 +1303,7 @@
   v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
   desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
                                                     0,
-                                                    PrePropertyHandlerHas);
+                                                    PrePropertyHandlerQuery);
   LocalContext env(NULL, desc->InstanceTemplate());
   Script::Compile(v8_str(
       "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
@@ -7076,6 +7143,163 @@
 }
 
 
+v8::Handle<Value> keyed_call_ic_function;
+
+static v8::Handle<Value> InterceptorKeyedCallICGetter(
+    Local<String> name, const AccessorInfo& info) {
+  ApiTestFuzzer::Fuzz();
+  if (v8_str("x")->Equals(name)) {
+    return keyed_call_ic_function;
+  }
+  return v8::Handle<Value>();
+}
+
+
+// Test the case when we stored cacheable lookup into
+// a stub, but the function name changed (to another cacheable function).
+THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
+  templ->SetNamedPropertyHandler(NoBlockGetterX);
+  LocalContext context;
+  context->Global()->Set(v8_str("o"), templ->NewInstance());
+  v8::Handle<Value> value = CompileRun(
+    "proto = new Object();"
+    "proto.y = function(x) { return x + 1; };"
+    "proto.z = function(x) { return x - 1; };"
+    "o.__proto__ = proto;"
+    "var result = 0;"
+    "var method = 'y';"
+    "for (var i = 0; i < 10; i++) {"
+    "  if (i == 5) { method = 'z'; };"
+    "  result += o[method](41);"
+    "}");
+  CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+
+// Test the case when we stored cacheable lookup into
+// a stub, but the function name changed (and the new function is present
+// both before and after the interceptor in the prototype chain).
+THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
+  templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
+  LocalContext context;
+  context->Global()->Set(v8_str("proto1"), templ->NewInstance());
+  keyed_call_ic_function =
+      v8_compile("function f(x) { return x - 1; }; f")->Run();
+  v8::Handle<Value> value = CompileRun(
+    "o = new Object();"
+    "proto2 = new Object();"
+    "o.y = function(x) { return x + 1; };"
+    "proto2.y = function(x) { return x + 2; };"
+    "o.__proto__ = proto1;"
+    "proto1.__proto__ = proto2;"
+    "var result = 0;"
+    "var method = 'x';"
+    "for (var i = 0; i < 10; i++) {"
+    "  if (i == 5) { method = 'y'; };"
+    "  result += o[method](41);"
+    "}");
+  CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+
+// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
+// on the global object.
+THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
+  templ->SetNamedPropertyHandler(NoBlockGetterX);
+  LocalContext context;
+  context->Global()->Set(v8_str("o"), templ->NewInstance());
+  v8::Handle<Value> value = CompileRun(
+    "function inc(x) { return x + 1; };"
+    "inc(1);"
+    "function dec(x) { return x - 1; };"
+    "dec(1);"
+    "o.__proto__ = this;"
+    "this.__proto__.x = inc;"
+    "this.__proto__.y = dec;"
+    "var result = 0;"
+    "var method = 'x';"
+    "for (var i = 0; i < 10; i++) {"
+    "  if (i == 5) { method = 'y'; };"
+    "  result += o[method](41);"
+    "}");
+  CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+
+// Test the case when actual function to call sits on global object.
+THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
+  templ_o->SetNamedPropertyHandler(NoBlockGetterX);
+  LocalContext context;
+  context->Global()->Set(v8_str("o"), templ_o->NewInstance());
+
+  v8::Handle<Value> value = CompileRun(
+    "function len(x) { return x.length; };"
+    "o.__proto__ = this;"
+    "var m = 'parseFloat';"
+    "var result = 0;"
+    "for (var i = 0; i < 10; i++) {"
+    "  if (i == 5) {"
+    "    m = 'len';"
+    "    saved_result = result;"
+    "  };"
+    "  result = o[m]('239');"
+    "}");
+  CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
+  CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
+}
+
+// Test the map transition before the interceptor.
+THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
+  templ_o->SetNamedPropertyHandler(NoBlockGetterX);
+  LocalContext context;
+  context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
+
+  v8::Handle<Value> value = CompileRun(
+    "var o = new Object();"
+    "o.__proto__ = proto;"
+    "o.method = function(x) { return x + 1; };"
+    "var m = 'method';"
+    "var result = 0;"
+    "for (var i = 0; i < 10; i++) {"
+    "  if (i == 5) { o.method = function(x) { return x - 1; }; };"
+    "  result += o[m](41);"
+    "}");
+  CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+
+// Test the map transition after the interceptor.
+THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
+  v8::HandleScope scope;
+  v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
+  templ_o->SetNamedPropertyHandler(NoBlockGetterX);
+  LocalContext context;
+  context->Global()->Set(v8_str("o"), templ_o->NewInstance());
+
+  v8::Handle<Value> value = CompileRun(
+    "var proto = new Object();"
+    "o.__proto__ = proto;"
+    "proto.method = function(x) { return x + 1; };"
+    "var m = 'method';"
+    "var result = 0;"
+    "for (var i = 0; i < 10; i++) {"
+    "  if (i == 5) { proto.method = function(x) { return x - 1; }; };"
+    "  result += o[m](41);"
+    "}");
+  CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+
 static int interceptor_call_count = 0;
 
 static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index 4b4c950..612e4fc 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -27,6 +27,8 @@
 
 #include <stdlib.h>
 
+#define USE_NEW_QUERY_CALLBACKS
+
 #include "v8.h"
 
 #include "api.h"
@@ -395,8 +397,9 @@
 
 
 static Handle<Code> ComputeCallDebugBreak(int argc) {
-  CALL_HEAP_FUNCTION(v8::internal::StubCache::ComputeCallDebugBreak(argc),
-                     Code);
+  CALL_HEAP_FUNCTION(
+      v8::internal::StubCache::ComputeCallDebugBreak(argc, Code::CALL_IC),
+      Code);
 }
 
 
diff --git a/test/cctest/test-decls.cc b/test/cctest/test-decls.cc
index f083027..c4be35e 100644
--- a/test/cctest/test-decls.cc
+++ b/test/cctest/test-decls.cc
@@ -27,6 +27,8 @@
 
 #include <stdlib.h>
 
+#define USE_NEW_QUERY_CALLBACKS
+
 #include "v8.h"
 
 #include "heap.h"
@@ -63,12 +65,12 @@
 
   int get_count() const { return get_count_; }
   int set_count() const { return set_count_; }
-  int has_count() const { return has_count_; }
+  int query_count() const { return query_count_; }
 
  protected:
   virtual v8::Handle<Value> Get(Local<String> key);
   virtual v8::Handle<Value> Set(Local<String> key, Local<Value> value);
-  virtual v8::Handle<Boolean> Has(Local<String> key);
+  virtual v8::Handle<Integer> Query(Local<String> key);
 
   void InitializeIfNeeded();
 
@@ -85,8 +87,8 @@
   static v8::Handle<Value> HandleSet(Local<String> key,
                                      Local<Value> value,
                                      const AccessorInfo& info);
-  static v8::Handle<Boolean> HandleHas(Local<String> key,
-                                       const AccessorInfo& info);
+  static v8::Handle<Integer> HandleQuery(Local<String> key,
+                                         const AccessorInfo& info);
 
  private:
   bool is_initialized_;
@@ -95,14 +97,14 @@
 
   int get_count_;
   int set_count_;
-  int has_count_;
+  int query_count_;
 
   static DeclarationContext* GetInstance(const AccessorInfo& info);
 };
 
 
 DeclarationContext::DeclarationContext()
-    : is_initialized_(false), get_count_(0), set_count_(0), has_count_(0) {
+    : is_initialized_(false), get_count_(0), set_count_(0), query_count_(0) {
   // Do nothing.
 }
 
@@ -114,7 +116,7 @@
   Local<Value> data = External::New(this);
   GetHolder(function)->SetNamedPropertyHandler(&HandleGet,
                                                &HandleSet,
-                                               &HandleHas,
+                                               &HandleQuery,
                                                0, 0,
                                                data);
   context_ = Context::New(0, function->InstanceTemplate(), Local<Value>());
@@ -124,7 +126,7 @@
 
 
 void DeclarationContext::Check(const char* source,
-                               int get, int set, int has,
+                               int get, int set, int query,
                                Expectations expectations,
                                v8::Handle<Value> value) {
   InitializeIfNeeded();
@@ -137,7 +139,7 @@
   Local<Value> result = Script::Compile(String::New(source))->Run();
   CHECK_EQ(get, get_count());
   CHECK_EQ(set, set_count());
-  CHECK_EQ(has, has_count());
+  CHECK_EQ(query, query_count());
   if (expectations == EXPECT_RESULT) {
     CHECK(!catcher.HasCaught());
     if (!value.IsEmpty()) {
@@ -170,11 +172,11 @@
 }
 
 
-v8::Handle<Boolean> DeclarationContext::HandleHas(Local<String> key,
-                                                  const AccessorInfo& info) {
+v8::Handle<Integer> DeclarationContext::HandleQuery(Local<String> key,
+                                                    const AccessorInfo& info) {
   DeclarationContext* context = GetInstance(info);
-  context->has_count_++;
-  return context->Has(key);
+  context->query_count_++;
+  return context->Query(key);
 }
 
 
@@ -194,8 +196,8 @@
 }
 
 
-v8::Handle<Boolean> DeclarationContext::Has(Local<String> key) {
-  return v8::Handle<Boolean>();
+v8::Handle<Integer> DeclarationContext::Query(Local<String> key) {
+  return v8::Handle<Integer>();
 }
 
 
@@ -249,8 +251,8 @@
 
 class PresentPropertyContext: public DeclarationContext {
  protected:
-  virtual v8::Handle<Boolean> Has(Local<String> key) {
-    return True();
+  virtual v8::Handle<Integer> Query(Local<String> key) {
+    return Integer::New(v8::None);
   }
 };
 
@@ -304,8 +306,8 @@
 
 class AbsentPropertyContext: public DeclarationContext {
  protected:
-  virtual v8::Handle<Boolean> Has(Local<String> key) {
-    return False();
+  virtual v8::Handle<Integer> Query(Local<String> key) {
+    return v8::Handle<Integer>();
   }
 };
 
@@ -316,7 +318,7 @@
   { AbsentPropertyContext context;
     context.Check("var x; x",
                   1,  // access
-                  2,  // declaration + initialization
+                  1,  // declaration
                   2,  // declaration + initialization
                   EXPECT_RESULT, Undefined());
   }
@@ -375,24 +377,24 @@
   AppearingPropertyContext() : state_(DECLARE) { }
 
  protected:
-  virtual v8::Handle<Boolean> Has(Local<String> key) {
+  virtual v8::Handle<Integer> Query(Local<String> key) {
     switch (state_) {
       case DECLARE:
         // Force declaration by returning that the
         // property is absent.
         state_ = INITIALIZE_IF_ASSIGN;
-        return False();
+        return Handle<Integer>();
       case INITIALIZE_IF_ASSIGN:
         // Return that the property is present so we only get the
         // setter called when initializing with a value.
         state_ = UNKNOWN;
-        return True();
+        return Integer::New(v8::None);
       default:
         CHECK(state_ == UNKNOWN);
         break;
     }
     // Do the lookup in the object.
-    return v8::Local<Boolean>();
+    return v8::Handle<Integer>();
   }
 
  private:
@@ -458,31 +460,31 @@
   ReappearingPropertyContext() : state_(DECLARE) { }
 
  protected:
-  virtual v8::Handle<Boolean> Has(Local<String> key) {
+  virtual v8::Handle<Integer> Query(Local<String> key) {
     switch (state_) {
       case DECLARE:
         // Force the first declaration by returning that
         // the property is absent.
         state_ = DONT_DECLARE;
-        return False();
+        return Handle<Integer>();
       case DONT_DECLARE:
         // Ignore the second declaration by returning
         // that the property is already there.
         state_ = INITIALIZE;
-        return True();
+        return Integer::New(v8::None);
       case INITIALIZE:
         // Force an initialization by returning that
         // the property is absent. This will make sure
         // that the setter is called and it will not
         // lead to redeclaration conflicts (yet).
         state_ = UNKNOWN;
-        return False();
+        return Handle<Integer>();
       default:
         CHECK(state_ == UNKNOWN);
         break;
     }
     // Do the lookup in the object.
-    return v8::Local<Boolean>();
+    return Handle<Integer>();
   }
 
  private:
@@ -506,9 +508,9 @@
 
 class ExistsInPrototypeContext: public DeclarationContext {
  protected:
-  virtual v8::Handle<Boolean> Has(Local<String> key) {
+  virtual v8::Handle<Integer> Query(Local<String> key) {
     // Let it seem that the property exists in the prototype object.
-    return True();
+    return Integer::New(v8::None);
   }
 
   // Use the prototype as the holder for the interceptors.
@@ -568,9 +570,9 @@
 
 class AbsentInPrototypeContext: public DeclarationContext {
  protected:
-  virtual v8::Handle<Boolean> Has(Local<String> key) {
+  virtual v8::Handle<Integer> Query(Local<String> key) {
     // Let it seem that the property is absent in the prototype object.
-    return False();
+    return Handle<Integer>();
   }
 
   // Use the prototype as the holder for the interceptors.
diff --git a/test/cctest/test-strings.cc b/test/cctest/test-strings.cc
index 0e30092..3a9e4da 100644
--- a/test/cctest/test-strings.cc
+++ b/test/cctest/test-strings.cc
@@ -433,3 +433,51 @@
   CHECK_EQ(0,
            v8::Script::Compile(v8::String::New(source))->Run()->Int32Value());
 }
+
+
+TEST(CachedHashOverflow) {
+  // We incorrectly allowed strings to be tagged as array indices even if their
+  // values didn't fit in the hash field.
+  // See http://code.google.com/p/v8/issues/detail?id=728
+  ZoneScope zone(DELETE_ON_EXIT);
+
+  InitializeVM();
+  v8::HandleScope handle_scope;
+  // Lines must be executed sequentially. Combining them into one script
+  // makes the bug go away.
+  const char* lines[] = {
+      "var x = [];",
+      "x[4] = 42;",
+      "var s = \"1073741828\";",
+      "x[s];",
+      "x[s] = 37;",
+      "x[4];",
+      "x[s];",
+      NULL
+  };
+
+  Handle<Smi> fortytwo(Smi::FromInt(42));
+  Handle<Smi> thirtyseven(Smi::FromInt(37));
+  Handle<Object> results[] = {
+      Factory::undefined_value(),
+      fortytwo,
+      Factory::undefined_value(),
+      Factory::undefined_value(),
+      thirtyseven,
+      fortytwo,
+      thirtyseven  // Bug yielded 42 here.
+  };
+
+  const char* line;
+  for (int i = 0; (line = lines[i]); i++) {
+    printf("%s\n", line);
+    v8::Local<v8::Value> result =
+        v8::Script::Compile(v8::String::New(line))->Run();
+    CHECK_EQ(results[i]->IsUndefined(), result->IsUndefined());
+    CHECK_EQ(results[i]->IsNumber(), result->IsNumber());
+    if (result->IsNumber()) {
+      CHECK_EQ(Smi::cast(results[i]->ToSmi())->value(),
+               result->ToInt32()->Value());
+    }
+  }
+}
diff --git a/test/cctest/test-utils.cc b/test/cctest/test-utils.cc
index 24b3c90..bcb185d 100644
--- a/test/cctest/test-utils.cc
+++ b/test/cctest/test-utils.cc
@@ -79,3 +79,55 @@
     buffer.Dispose();
   }
 }
+
+
+void TestMemCopy(Vector<byte> src,
+                 Vector<byte> dst,
+                 int source_alignment,
+                 int destination_alignment,
+                 int length_alignment) {
+  memset(dst.start(), 0xFF, dst.length());
+  byte* to = dst.start() + 32 + destination_alignment;
+  byte* from = src.start() + source_alignment;
+  int length = kMinComplexMemCopy + length_alignment;
+  MemCopy(to, from, static_cast<size_t>(length));
+  printf("[%d,%d,%d]\n",
+         source_alignment, destination_alignment, length_alignment);
+  for (int i = 0; i < length; i++) {
+    CHECK_EQ(from[i], to[i]);
+  }
+  CHECK_EQ(0xFF, to[-1]);
+  CHECK_EQ(0xFF, to[length]);
+}
+
+
+
+TEST(MemCopy) {
+  const int N = kMinComplexMemCopy + 128;
+  Vector<byte> buffer1 = Vector<byte>::New(N);
+  Vector<byte> buffer2 = Vector<byte>::New(N);
+
+  for (int i = 0; i < N; i++) {
+    buffer1[i] = static_cast<byte>(i & 0x7F);
+  }
+
+  // Same alignment.
+  for (int i = 0; i < 32; i++) {
+    TestMemCopy(buffer1, buffer2, i, i, i * 2);
+  }
+
+  // Different alignment.
+  for (int i = 0; i < 32; i++) {
+    for (int j = 1; j < 32; j++) {
+      TestMemCopy(buffer1, buffer2, i, (i + j) & 0x1F , 0);
+    }
+  }
+
+  // Different lengths
+  for (int i = 0; i < 32; i++) {
+    TestMemCopy(buffer1, buffer2, 3, 7, i);
+  }
+
+  buffer2.Dispose();
+  buffer1.Dispose();
+}