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();
+}
diff --git a/test/mjsunit/eval.js b/test/mjsunit/eval.js
index 95357c7..25cfcb6 100644
--- a/test/mjsunit/eval.js
+++ b/test/mjsunit/eval.js
@@ -50,7 +50,7 @@
assertEquals(void 0, eval(eval("var eval = function f(x) { return 'hest';}")))
eval = global_eval;
-//Test eval with different number of parameters.
+// Test eval with different number of parameters.
global_eval = eval;
eval = function(x, y) { return x + y; };
assertEquals(4, eval(2, 2));
diff --git a/test/mjsunit/keyed-call-ic.js b/test/mjsunit/keyed-call-ic.js
new file mode 100644
index 0000000..9d82965
--- /dev/null
+++ b/test/mjsunit/keyed-call-ic.js
@@ -0,0 +1,205 @@
+// Copyright 2010 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.
+
+// A test for keyed call ICs.
+
+var toStringName = 'toString';
+var global = this;
+
+function globalFunction1() {
+ return 'function1';
+}
+
+function globalFunction2() {
+ return 'function2';
+}
+
+assertEquals("[object global]", this[toStringName]());
+assertEquals("[object global]", global[toStringName]());
+
+function testGlobals() {
+ assertEquals("[object global]", this[toStringName]());
+ assertEquals("[object global]", global[toStringName]());
+}
+
+testGlobals();
+
+
+function F() {}
+
+F.prototype.one = function() {return 'one'; }
+F.prototype.two = function() {return 'two'; }
+F.prototype.three = function() {return 'three'; }
+
+var keys =
+ ['one', 'one', 'one', 'one', 'two', 'two', 'one', 'three', 'one', 'two'];
+
+function testKeyTransitions() {
+ var i, key, result, message;
+
+ var f = new F();
+
+ // Custom call generators
+ var array = [];
+ for (i = 0; i != 10; i++) {
+ key = (i < 8) ? 'push' : 'pop';
+ array[key](i);
+ }
+
+ assertEquals(6, array.length);
+ for (i = 0; i != array.length; i++) {
+ assertEquals(i, array[i]);
+ }
+
+ for (i = 0; i != 10; i++) {
+ key = (i < 3) ? 'pop' : 'push';
+ array[key](i);
+ }
+
+ assertEquals(10, array.length);
+ for (i = 0; i != array.length; i++) {
+ assertEquals(i, array[i]);
+ }
+
+ var string = 'ABCDEFGHIJ';
+ for (i = 0; i != 10; i++) {
+ key = ((i < 5) ? 'charAt' : 'charCodeAt');
+ result = string[key](i);
+ message = '\'' + string + '\'[\'' + key + '\'](' + i + ')';
+ if (i < 5) {
+ assertEquals(string.charAt(i), result, message);
+ } else {
+ assertEquals(string.charCodeAt(i), result, message);
+ }
+ }
+
+ for (i = 0; i != 10; i++) {
+ key = ((i < 5) ? 'charCodeAt' : 'charAt');
+ result = string[key](i);
+ message = '\'' + string + '\'[\'' + key + '\'](' + i + ')';
+ if (i < 5) {
+ assertEquals(string.charCodeAt(i), result, message);
+ } else {
+ assertEquals(string.charAt(i), result, message);
+ }
+ }
+
+ // Function is a constant property
+ key = 'one';
+ for (i = 0; i != 10; i++) {
+ assertEquals(key, f[key]());
+ if (i == 5) {
+ key = 'two'; // the name change should case a miss
+ }
+ }
+
+ // Function is a fast property
+ f.field = function() { return 'field'; }
+ key = 'field';
+ for (i = 0; i != 10; i++) {
+ assertEquals(key, f[key]());
+ if (i == 5) {
+ key = 'two'; // the name change should case a miss
+ }
+ }
+
+ // Calling on slow case object
+ f.prop = 0;
+ delete f.prop; // force the object to the slow case
+ f.four = function() { return 'four'; }
+ f.five = function() { return 'five'; }
+
+ key = 'four';
+ for (i = 0; i != 10; i++) {
+ assertEquals(key, f[key]());
+ if (i == 5) {
+ key = 'five';
+ }
+ }
+
+ // Calling on global object
+ key = 'globalFunction1';
+ var expect = 'function1';
+ for (i = 0; i != 10; i++) {
+ assertEquals(expect, global[key]());
+ if (i == 5) {
+ key = 'globalFunction2';
+ expect = 'function2';
+ }
+ }
+}
+
+testKeyTransitions();
+
+function testTypeTransitions() {
+ var f = new F();
+ var s = '';
+ var m = 'one';
+ var i;
+
+ s = '';
+ for (i = 0; i != 10; i++) {
+ if (i == 5) { F.prototype.one = function() { return '1'; } }
+ s += f[m]();
+ }
+ assertEquals("oneoneoneoneone11111", s);
+
+ s = '';
+ for (i = 0; i != 10; i++) {
+ if (i == 5) { f.__proto__ = { one: function() { return 'I'; } } }
+ s += f[m]();
+ }
+ assertEquals("11111IIIII", s);
+
+ s = '';
+ for (i = 0; i != 10; i++) {
+ if (i == 5) { f.one = function() { return 'ONE'; } }
+ s += f[m]();
+ }
+ assertEquals("IIIIIONEONEONEONEONE", s);
+
+ m = 'toString';
+
+ s = '';
+ var obj = { toString: function() { return '2'; } };
+ for (i = 0; i != 10; i++) {
+ if (i == 5) { obj = "TWO"; }
+ s += obj[m]();
+ }
+ assertEquals("22222TWOTWOTWOTWOTWO", s);
+
+ s = '';
+ obj = { toString: function() { return 'ONE'; } };
+ m = 'toString';
+ for (i = 0; i != 10; i++) {
+ if (i == 5) { obj = 1; }
+ s += obj[m]();
+ }
+ assertEquals("ONEONEONEONEONE11111", s);
+}
+
+testTypeTransitions();
diff --git a/test/mjsunit/regress/regress-728.js b/test/mjsunit/regress/regress-728.js
new file mode 100644
index 0000000..196b118
--- /dev/null
+++ b/test/mjsunit/regress/regress-728.js
@@ -0,0 +1,42 @@
+// Copyright 2010 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.
+
+var obj = { 0: "obj0" };
+
+// Array index k is to big to fit into the string hash field.
+var k = 16777217;
+var h = "" + k;
+
+obj[k] = "obj" + k;
+
+// Force computation of hash for the string representation of array index.
+for (var i = 0; i < 10; i++) { ({})[h]; }
+
+function get(idx) { return obj[idx]; }
+
+assertEquals(get(0), "obj0");
+assertEquals(get(h), "obj" + h);
diff --git a/test/mjsunit/regress/regress-732.js b/test/mjsunit/regress/regress-732.js
new file mode 100644
index 0000000..2b02ea6
--- /dev/null
+++ b/test/mjsunit/regress/regress-732.js
@@ -0,0 +1,46 @@
+// Copyright 2010 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.
+
+// idx is a valid array index but is too big to be cached in hash field.
+var idx = 10000000;
+
+// Create a JSObject with NumberDictionary as a backing store for elements.
+var obj = { };
+for (var i = 0; i < 100000; i += 100) { obj[i] = "obj" + i; }
+
+// Set value using numeric index.
+obj[idx] = "obj" + idx;
+
+// Make a string from index.
+var str = "" + idx;
+
+// Force hash computation for the string representation of index.
+for (var i = 0; i < 10; i++) { ({})[str]; }
+
+// Try getting value back using string and number representations of
+// the same index.
+assertEquals(obj[str], obj[idx])
diff --git a/test/mjsunit/string-charat.js b/test/mjsunit/string-charat.js
index f0b0566..5ce4e89 100644
--- a/test/mjsunit/string-charat.js
+++ b/test/mjsunit/string-charat.js
@@ -27,52 +27,58 @@
var s = "test";
+function getTwoByteString() { return "\u1234t"; }
+function getCons() { return "testtesttesttest" + getTwoByteString() }
+
var slowIndex1 = { valueOf: function() { return 1; } };
var slowIndex2 = { toString: function() { return "2"; } };
var slowIndexOutOfRange = { valueOf: function() { return -1; } };
-function basicTest() {
- assertEquals("t", s.charAt());
- assertEquals("t", s.charAt("string"));
- assertEquals("t", s.charAt(null));
- assertEquals("t", s.charAt(void 0));
- assertEquals("t", s.charAt(false));
- assertEquals("e", s.charAt(true));
- assertEquals("", s.charAt(-1));
- assertEquals("", s.charAt(4));
- assertEquals("", s.charAt(slowIndexOutOfRange));
- assertEquals("", s.charAt(1/0));
- assertEquals("", s.charAt(-1/0));
- assertEquals("t", s.charAt(0));
- assertEquals("t", s.charAt(-0.0));
- assertEquals("t", s.charAt(0.4));
- assertEquals("e", s.charAt(slowIndex1));
- assertEquals("s", s.charAt(slowIndex2));
- assertEquals("t", s.charAt(3));
- assertEquals("t", s.charAt(3.4));
- assertEquals("t", s.charAt(NaN));
+function basicTest(s, len) {
+ assertEquals("t", s().charAt());
+ assertEquals("t", s().charAt("string"));
+ assertEquals("t", s().charAt(null));
+ assertEquals("t", s().charAt(void 0));
+ assertEquals("t", s().charAt(false));
+ assertEquals("e", s().charAt(true));
+ assertEquals("", s().charAt(-1));
+ assertEquals("", s().charAt(len));
+ assertEquals("", s().charAt(slowIndexOutOfRange));
+ assertEquals("", s().charAt(1/0));
+ assertEquals("", s().charAt(-1/0));
+ assertEquals("t", s().charAt(0));
+ assertEquals("t", s().charAt(-0.0));
+ assertEquals("t", s().charAt(-0.1));
+ assertEquals("t", s().charAt(0.4));
+ assertEquals("e", s().charAt(slowIndex1));
+ assertEquals("s", s().charAt(slowIndex2));
+ assertEquals("t", s().charAt(3));
+ assertEquals("t", s().charAt(3.4));
+ assertEquals("t", s().charAt(NaN));
- assertEquals(116, s.charCodeAt());
- assertEquals(116, s.charCodeAt("string"));
- assertEquals(116, s.charCodeAt(null));
- assertEquals(116, s.charCodeAt(void 0));
- assertEquals(116, s.charCodeAt(false));
- assertEquals(101, s.charCodeAt(true));
- assertEquals(116, s.charCodeAt(0));
- assertEquals(116, s.charCodeAt(-0.0));
- assertEquals(116, s.charCodeAt(0.4));
- assertEquals(101, s.charCodeAt(slowIndex1));
- assertEquals(115, s.charCodeAt(slowIndex2));
- assertEquals(116, s.charCodeAt(3));
- assertEquals(116, s.charCodeAt(3.4));
- assertEquals(116, s.charCodeAt(NaN));
- assertTrue(isNaN(s.charCodeAt(-1)));
- assertTrue(isNaN(s.charCodeAt(4)));
- assertTrue(isNaN(s.charCodeAt(slowIndexOutOfRange)));
- assertTrue(isNaN(s.charCodeAt(1/0)));
- assertTrue(isNaN(s.charCodeAt(-1/0)));
+ assertEquals(116, s().charCodeAt());
+ assertEquals(116, s().charCodeAt("string"));
+ assertEquals(116, s().charCodeAt(null));
+ assertEquals(116, s().charCodeAt(void 0));
+ assertEquals(116, s().charCodeAt(false));
+ assertEquals(101, s().charCodeAt(true));
+ assertEquals(116, s().charCodeAt(0));
+ assertEquals(116, s().charCodeAt(-0.0));
+ assertEquals(116, s().charCodeAt(-0.1));
+ assertEquals(116, s().charCodeAt(0.4));
+ assertEquals(101, s().charCodeAt(slowIndex1));
+ assertEquals(115, s().charCodeAt(slowIndex2));
+ assertEquals(116, s().charCodeAt(3));
+ assertEquals(116, s().charCodeAt(3.4));
+ assertEquals(116, s().charCodeAt(NaN));
+ assertTrue(isNaN(s().charCodeAt(-1)));
+ assertTrue(isNaN(s().charCodeAt(len)));
+ assertTrue(isNaN(s().charCodeAt(slowIndexOutOfRange)));
+ assertTrue(isNaN(s().charCodeAt(1/0)));
+ assertTrue(isNaN(s().charCodeAt(-1/0)));
}
-basicTest();
+basicTest(function() { return s; }, s.length);
+basicTest(getCons, getCons().length);
// Make sure enough of the one-char string cache is filled.
var alpha = ['@'];
@@ -122,7 +128,8 @@
// Test custom string IC-s.
for (var i = 0; i < 20; i++) {
- basicTest();
+ basicTest(function() { return s; }, s.length);
+ basicTest(getCons, getCons().length);
stealTest();
}
diff --git a/test/mjsunit/string-charcodeat.js b/test/mjsunit/string-charcodeat.js
index 3927557..831f688 100644
--- a/test/mjsunit/string-charcodeat.js
+++ b/test/mjsunit/string-charcodeat.js
@@ -153,6 +153,10 @@
TestStringType(Flat16, true);
TestStringType(NotAString16, true);
+for (var i = 0; i != 10; i++) {
+ assertEquals(101, Cons16().charCodeAt(1.1));
+ assertEquals('e', Cons16().charAt(1.1));
+}
function StupidThing() {
// Doesn't return a string from toString!