diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2009-10-28 19:25:22 +0100 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2009-10-28 19:25:22 +0100 |
commit | 50f45d14b475a42d304e7d9872f8d91ff3a013c2 (patch) | |
tree | 2e799be8cbddd016ef8432b4ed755247a466c0ba /deps/v8/test/cctest | |
parent | 35589528992e8bf5ca70271beaef05a6d82f9dcf (diff) | |
download | node-new-50f45d14b475a42d304e7d9872f8d91ff3a013c2.tar.gz |
Upgrade v8 to 1.3.17
Diffstat (limited to 'deps/v8/test/cctest')
-rw-r--r-- | deps/v8/test/cctest/SConscript | 1 | ||||
-rw-r--r-- | deps/v8/test/cctest/cctest.cc | 3 | ||||
-rw-r--r-- | deps/v8/test/cctest/cctest.h | 136 | ||||
-rw-r--r-- | deps/v8/test/cctest/cctest.status | 15 | ||||
-rw-r--r-- | deps/v8/test/cctest/test-accessors.cc | 424 | ||||
-rw-r--r-- | deps/v8/test/cctest/test-api.cc | 813 | ||||
-rw-r--r-- | deps/v8/test/cctest/test-debug.cc | 6 | ||||
-rw-r--r-- | deps/v8/test/cctest/test-log-stack-tracer.cc | 22 | ||||
-rw-r--r-- | deps/v8/test/cctest/test-log.cc | 5 | ||||
-rwxr-xr-x | deps/v8/test/cctest/test-macro-assembler-x64.cc | 10 | ||||
-rw-r--r-- | deps/v8/test/cctest/test-mark-compact.cc | 5 | ||||
-rw-r--r-- | deps/v8/test/cctest/test-serialize.cc | 70 | ||||
-rw-r--r-- | deps/v8/test/cctest/test-spaces.cc | 20 |
13 files changed, 1151 insertions, 379 deletions
diff --git a/deps/v8/test/cctest/SConscript b/deps/v8/test/cctest/SConscript index f041041c11..9deefa5542 100644 --- a/deps/v8/test/cctest/SConscript +++ b/deps/v8/test/cctest/SConscript @@ -34,6 +34,7 @@ Import('context object_files') SOURCES = { 'all': [ + 'test-accessors.cc', 'test-alloc.cc', 'test-api.cc', 'test-ast.cc', diff --git a/deps/v8/test/cctest/cctest.cc b/deps/v8/test/cctest/cctest.cc index 82a33e6da5..f638ed480f 100644 --- a/deps/v8/test/cctest/cctest.cc +++ b/deps/v8/test/cctest/cctest.cc @@ -121,3 +121,6 @@ int main(int argc, char* argv[]) { v8::V8::Dispose(); return 0; } + +RegisterThreadedTest *RegisterThreadedTest::first_ = NULL; +int RegisterThreadedTest::count_ = 0; diff --git a/deps/v8/test/cctest/cctest.h b/deps/v8/test/cctest/cctest.h index a95645e010..404b692b27 100644 --- a/deps/v8/test/cctest/cctest.h +++ b/deps/v8/test/cctest/cctest.h @@ -28,6 +28,8 @@ #ifndef CCTEST_H_ #define CCTEST_H_ +#include "v8.h" + #ifndef TEST #define TEST(Name) \ static void Test##Name(); \ @@ -72,4 +74,138 @@ class CcTest { CcTest* prev_; }; +// Switches between all the Api tests using the threading support. +// In order to get a surprising but repeatable pattern of thread +// switching it has extra semaphores to control the order in which +// the tests alternate, not relying solely on the big V8 lock. +// +// A test is augmented with calls to ApiTestFuzzer::Fuzz() in its +// callbacks. This will have no effect when we are not running the +// thread fuzzing test. In the thread fuzzing test it will +// pseudorandomly select a successor thread and switch execution +// to that thread, suspending the current test. +class ApiTestFuzzer: public v8::internal::Thread { + public: + void CallTest(); + explicit ApiTestFuzzer(int num) + : test_number_(num), + gate_(v8::internal::OS::CreateSemaphore(0)), + active_(true) { + } + ~ApiTestFuzzer() { delete gate_; } + + // The ApiTestFuzzer is also a Thread, so it has a Run method. + virtual void Run(); + + enum PartOfTest { FIRST_PART, SECOND_PART }; + + static void Setup(PartOfTest part); + static void RunAllTests(); + static void TearDown(); + // This method switches threads if we are running the Threading test. + // Otherwise it does nothing. + static void Fuzz(); + private: + static bool fuzzing_; + static int tests_being_run_; + static int current_; + static int active_tests_; + static bool NextThread(); + int test_number_; + v8::internal::Semaphore* gate_; + bool active_; + void ContextSwitch(); + static int GetNextTestNumber(); + static v8::internal::Semaphore* all_tests_done_; +}; + + +#define THREADED_TEST(Name) \ + static void Test##Name(); \ + RegisterThreadedTest register_##Name(Test##Name, #Name); \ + /* */ TEST(Name) + + +class RegisterThreadedTest { + public: + explicit RegisterThreadedTest(CcTest::TestFunction* callback, + const char* name) + : fuzzer_(NULL), callback_(callback), name_(name) { + prev_ = first_; + first_ = this; + count_++; + } + static int count() { return count_; } + static RegisterThreadedTest* nth(int i) { + CHECK(i < count()); + RegisterThreadedTest* current = first_; + while (i > 0) { + i--; + current = current->prev_; + } + return current; + } + CcTest::TestFunction* callback() { return callback_; } + ApiTestFuzzer* fuzzer_; + const char* name() { return name_; } + + private: + static RegisterThreadedTest* first_; + static int count_; + CcTest::TestFunction* callback_; + RegisterThreadedTest* prev_; + const char* name_; +}; + + +// A LocalContext holds a reference to a v8::Context. +class LocalContext { + public: + LocalContext(v8::ExtensionConfiguration* extensions = 0, + v8::Handle<v8::ObjectTemplate> global_template = + v8::Handle<v8::ObjectTemplate>(), + v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>()) + : context_(v8::Context::New(extensions, global_template, global_object)) { + context_->Enter(); + } + + virtual ~LocalContext() { + context_->Exit(); + context_.Dispose(); + } + + v8::Context* operator->() { return *context_; } + v8::Context* operator*() { return *context_; } + bool IsReady() { return !context_.IsEmpty(); } + + v8::Local<v8::Context> local() { + return v8::Local<v8::Context>::New(context_); + } + + private: + v8::Persistent<v8::Context> context_; +}; + + +static inline v8::Local<v8::Value> v8_num(double x) { + return v8::Number::New(x); +} + + +static inline v8::Local<v8::String> v8_str(const char* x) { + return v8::String::New(x); +} + + +static inline v8::Local<v8::Script> v8_compile(const char* x) { + return v8::Script::Compile(v8_str(x)); +} + + +// Helper function that compiles and runs the source. +static inline v8::Local<v8::Value> CompileRun(const char* source) { + return v8::Script::Compile(v8::String::New(source))->Run(); +} + + #endif // ifndef CCTEST_H_ diff --git a/deps/v8/test/cctest/cctest.status b/deps/v8/test/cctest/cctest.status index 8fff76933b..6ce241ff1e 100644 --- a/deps/v8/test/cctest/cctest.status +++ b/deps/v8/test/cctest/cctest.status @@ -33,9 +33,24 @@ test-debug/DebuggerAgent: PASS, (PASS || FAIL) if $system == linux # BUG(382): Weird test. Can't guarantee that it never times out. test-api/ApplyInterruption: PASS || TIMEOUT +# This is about to go away anyway since new snapshot code is on the way. +test-serialize/Deserialize: FAIL +test-serialize/DeserializeAndRunScript: FAIL +test-serialize/DeserializeNatives: FAIL +test-serialize/DeserializeExtensions: FAIL + +# These tests always fail. They are here to test test.py. If +# they don't fail then test.py has failed. +test-serialize/TestThatAlwaysFails: FAIL +test-serialize/DependentTestThatAlwaysFails: FAIL + [ $arch == arm ] +# New serialization doesn't work on ARM yet. +test-serialize/Deserialize2: SKIP +test-serialize/DeserializeAndRunScript2: SKIP + # BUG(113): Test seems flaky on ARM. test-spaces/LargeObjectSpace: PASS || FAIL diff --git a/deps/v8/test/cctest/test-accessors.cc b/deps/v8/test/cctest/test-accessors.cc new file mode 100644 index 0000000000..b56238ad69 --- /dev/null +++ b/deps/v8/test/cctest/test-accessors.cc @@ -0,0 +1,424 @@ +// 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 <stdlib.h> + +#include "v8.h" + +#include "api.h" +#include "cctest.h" +#include "frames-inl.h" +#include "string-stream.h" + +using ::v8::ObjectTemplate; +using ::v8::Value; +using ::v8::Context; +using ::v8::Local; +using ::v8::String; +using ::v8::Script; +using ::v8::Function; +using ::v8::AccessorInfo; +using ::v8::Extension; + +namespace i = ::v8::internal; + +static v8::Handle<Value> handle_property(Local<String> name, + const AccessorInfo&) { + ApiTestFuzzer::Fuzz(); + return v8_num(900); +} + + +THREADED_TEST(PropertyHandler) { + v8::HandleScope scope; + Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); + fun_templ->InstanceTemplate()->SetAccessor(v8_str("foo"), handle_property); + LocalContext env; + Local<Function> fun = fun_templ->GetFunction(); + env->Global()->Set(v8_str("Fun"), fun); + Local<Script> getter = v8_compile("var obj = new Fun(); obj.foo;"); + CHECK_EQ(900, getter->Run()->Int32Value()); + Local<Script> setter = v8_compile("obj.foo = 901;"); + CHECK_EQ(901, setter->Run()->Int32Value()); +} + + +static v8::Handle<Value> GetIntValue(Local<String> property, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + int* value = + static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); + return v8_num(*value); +} + + +static void SetIntValue(Local<String> property, + Local<Value> value, + const AccessorInfo& info) { + int* field = + static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); + *field = value->Int32Value(); +} + +int foo, bar, baz; + +THREADED_TEST(GlobalVariableAccess) { + foo = 0; + bar = -4; + baz = 10; + v8::HandleScope scope; + v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); + templ->InstanceTemplate()->SetAccessor(v8_str("foo"), + GetIntValue, + SetIntValue, + v8::External::New(&foo)); + templ->InstanceTemplate()->SetAccessor(v8_str("bar"), + GetIntValue, + SetIntValue, + v8::External::New(&bar)); + templ->InstanceTemplate()->SetAccessor(v8_str("baz"), + GetIntValue, + SetIntValue, + v8::External::New(&baz)); + LocalContext env(0, templ->InstanceTemplate()); + v8_compile("foo = (++bar) + baz")->Run(); + CHECK_EQ(bar, -3); + CHECK_EQ(foo, 7); +} + + +static int x_register = 0; +static v8::Handle<v8::Object> x_receiver; +static v8::Handle<v8::Object> x_holder; + + +static v8::Handle<Value> XGetter(Local<String> name, const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + CHECK_EQ(x_receiver, info.This()); + CHECK_EQ(x_holder, info.Holder()); + return v8_num(x_register); +} + + +static void XSetter(Local<String> name, + Local<Value> value, + const AccessorInfo& info) { + CHECK_EQ(x_holder, info.This()); + CHECK_EQ(x_holder, info.Holder()); + x_register = value->Int32Value(); +} + + +THREADED_TEST(AccessorIC) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("x"), XGetter, XSetter); + LocalContext context; + x_holder = obj->NewInstance(); + context->Global()->Set(v8_str("holder"), x_holder); + x_receiver = v8::Object::New(); + context->Global()->Set(v8_str("obj"), x_receiver); + v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(CompileRun( + "obj.__proto__ = holder;" + "var result = [];" + "for (var i = 0; i < 10; i++) {" + " holder.x = i;" + " result.push(obj.x);" + "}" + "result")); + CHECK_EQ(10, array->Length()); + for (int i = 0; i < 10; i++) { + v8::Handle<Value> entry = array->Get(v8::Integer::New(i)); + CHECK_EQ(v8::Integer::New(i), entry); + } +} + + +static v8::Handle<Value> AccessorProhibitsOverwritingGetter( + Local<String> name, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + return v8::True(); +} + + +THREADED_TEST(AccessorProhibitsOverwriting) { + v8::HandleScope scope; + LocalContext context; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetAccessor(v8_str("x"), + AccessorProhibitsOverwritingGetter, + 0, + v8::Handle<Value>(), + v8::PROHIBITS_OVERWRITING, + v8::ReadOnly); + Local<v8::Object> instance = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), instance); + Local<Value> value = CompileRun( + "obj.__defineGetter__('x', function() { return false; });" + "obj.x"); + CHECK(value->BooleanValue()); + value = CompileRun( + "var setter_called = false;" + "obj.__defineSetter__('x', function() { setter_called = true; });" + "obj.x = 42;" + "setter_called"); + CHECK(!value->BooleanValue()); + value = CompileRun( + "obj2 = {};" + "obj2.__proto__ = obj;" + "obj2.__defineGetter__('x', function() { return false; });" + "obj2.x"); + CHECK(value->BooleanValue()); + value = CompileRun( + "var setter_called = false;" + "obj2 = {};" + "obj2.__proto__ = obj;" + "obj2.__defineSetter__('x', function() { setter_called = true; });" + "obj2.x = 42;" + "setter_called"); + CHECK(!value->BooleanValue()); +} + + +template <int C> +static v8::Handle<Value> HandleAllocatingGetter(Local<String> name, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + for (int i = 0; i < C; i++) + v8::String::New("foo"); + return v8::String::New("foo"); +} + + +THREADED_TEST(HandleScopePop) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("one"), HandleAllocatingGetter<1>); + obj->SetAccessor(v8_str("many"), HandleAllocatingGetter<1024>); + LocalContext context; + v8::Handle<v8::Object> inst = obj->NewInstance(); + context->Global()->Set(v8::String::New("obj"), inst); + int count_before = i::HandleScope::NumberOfHandles(); + { + v8::HandleScope scope; + CompileRun( + "for (var i = 0; i < 1000; i++) {" + " obj.one;" + " obj.many;" + "}"); + } + int count_after = i::HandleScope::NumberOfHandles(); + CHECK_EQ(count_before, count_after); +} + +static v8::Handle<Value> CheckAccessorArgsCorrect(Local<String> name, + const AccessorInfo& info) { + CHECK(info.This() == info.Holder()); + CHECK(info.Data()->Equals(v8::String::New("data"))); + ApiTestFuzzer::Fuzz(); + CHECK(info.This() == info.Holder()); + CHECK(info.Data()->Equals(v8::String::New("data"))); + i::Heap::CollectAllGarbage(true); + CHECK(info.This() == info.Holder()); + CHECK(info.Data()->Equals(v8::String::New("data"))); + return v8::Integer::New(17); +} + +THREADED_TEST(DirectCall) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("xxx"), + CheckAccessorArgsCorrect, + NULL, + v8::String::New("data")); + LocalContext context; + v8::Handle<v8::Object> inst = obj->NewInstance(); + context->Global()->Set(v8::String::New("obj"), inst); + Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); + for (int i = 0; i < 10; i++) { + Local<Value> result = scr->Run(); + CHECK(!result.IsEmpty()); + CHECK_EQ(17, result->Int32Value()); + } +} + +static v8::Handle<Value> EmptyGetter(Local<String> name, + const AccessorInfo& info) { + CheckAccessorArgsCorrect(name, info); + ApiTestFuzzer::Fuzz(); + CheckAccessorArgsCorrect(name, info); + return v8::Handle<v8::Value>(); +} + +THREADED_TEST(EmptyResult) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data")); + LocalContext context; + v8::Handle<v8::Object> inst = obj->NewInstance(); + context->Global()->Set(v8::String::New("obj"), inst); + Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); + for (int i = 0; i < 10; i++) { + Local<Value> result = scr->Run(); + CHECK(result == v8::Undefined()); + } +} + + +THREADED_TEST(NoReuseRegress) { + // Check that the IC generated for the one test doesn't get reused + // for the other. + v8::HandleScope scope; + { + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data")); + LocalContext context; + v8::Handle<v8::Object> inst = obj->NewInstance(); + context->Global()->Set(v8::String::New("obj"), inst); + Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); + for (int i = 0; i < 2; i++) { + Local<Value> result = scr->Run(); + CHECK(result == v8::Undefined()); + } + } + { + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("xxx"), + CheckAccessorArgsCorrect, + NULL, + v8::String::New("data")); + LocalContext context; + v8::Handle<v8::Object> inst = obj->NewInstance(); + context->Global()->Set(v8::String::New("obj"), inst); + Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx")); + for (int i = 0; i < 10; i++) { + Local<Value> result = scr->Run(); + CHECK(!result.IsEmpty()); + CHECK_EQ(17, result->Int32Value()); + } + } +} + +static v8::Handle<Value> ThrowingGetAccessor(Local<String> name, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + return v8::ThrowException(v8_str("g")); +} + + +static void ThrowingSetAccessor(Local<String> name, + Local<Value> value, + const AccessorInfo& info) { + v8::ThrowException(value); +} + + +THREADED_TEST(Regress1054726) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("x"), + ThrowingGetAccessor, + ThrowingSetAccessor, + Local<Value>()); + + LocalContext env; + env->Global()->Set(v8_str("obj"), obj->NewInstance()); + + // Use the throwing property setter/getter in a loop to force + // the accessor ICs to be initialized. + v8::Handle<Value> result; + result = Script::Compile(v8_str( + "var result = '';" + "for (var i = 0; i < 5; i++) {" + " try { obj.x; } catch (e) { result += e; }" + "}; result"))->Run(); + CHECK_EQ(v8_str("ggggg"), result); + + result = Script::Compile(String::New( + "var result = '';" + "for (var i = 0; i < 5; i++) {" + " try { obj.x = i; } catch (e) { result += e; }" + "}; result"))->Run(); + CHECK_EQ(v8_str("01234"), result); +} + + +static v8::Handle<Value> AllocGetter(Local<String> name, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + return v8::Array::New(1000); +} + + +THREADED_TEST(Gc) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + obj->SetAccessor(v8_str("xxx"), AllocGetter); + LocalContext env; + env->Global()->Set(v8_str("obj"), obj->NewInstance()); + Script::Compile(String::New( + "var last = [];" + "for (var i = 0; i < 2048; i++) {" + " var result = obj.xxx;" + " result[0] = last;" + " last = result;" + "}"))->Run(); +} + + +static v8::Handle<Value> StackCheck(Local<String> name, + const AccessorInfo& info) { + i::StackFrameIterator iter; + for (int i = 0; !iter.done(); i++) { + i::StackFrame* frame = iter.frame(); + CHECK(i != 0 || (frame->type() == i::StackFrame::EXIT)); + CHECK(frame->code()->IsCode()); + i::Address pc = frame->pc(); + i::Code* code = frame->code(); + CHECK(code->contains(pc)); + iter.Advance(); + } + return v8::Undefined(); +} + + +THREADED_TEST(StackIteration) { + v8::HandleScope scope; + v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); + i::StringStream::ClearMentionedObjectCache(); + obj->SetAccessor(v8_str("xxx"), StackCheck); + LocalContext env; + env->Global()->Set(v8_str("obj"), obj->NewInstance()); + Script::Compile(String::New( + "function foo() {" + " return obj.xxx;" + "}" + "for (var i = 0; i < 100; i++) {" + " foo();" + "}"))->Run(); +} diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index c63ba31652..83038ae1a7 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -25,6 +25,8 @@ // (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 <limits.h> + #include "v8.h" #include "api.h" @@ -33,8 +35,11 @@ #include "snapshot.h" #include "platform.h" #include "top.h" +#include "utils.h" #include "cctest.h" +static const bool kLogThreading = false; + static bool IsNaN(double x) { #ifdef WIN32 return _isnan(x); @@ -55,131 +60,6 @@ using ::v8::Extension; namespace i = ::v8::internal; -static Local<Value> v8_num(double x) { - return v8::Number::New(x); -} - - -static Local<String> v8_str(const char* x) { - return String::New(x); -} - - -static Local<Script> v8_compile(const char* x) { - return Script::Compile(v8_str(x)); -} - - -// A LocalContext holds a reference to a v8::Context. -class LocalContext { - public: - LocalContext(v8::ExtensionConfiguration* extensions = 0, - v8::Handle<ObjectTemplate> global_template = - v8::Handle<ObjectTemplate>(), - v8::Handle<Value> global_object = v8::Handle<Value>()) - : context_(Context::New(extensions, global_template, global_object)) { - context_->Enter(); - } - - virtual ~LocalContext() { - context_->Exit(); - context_.Dispose(); - } - - Context* operator->() { return *context_; } - Context* operator*() { return *context_; } - Local<Context> local() { return Local<Context>::New(context_); } - bool IsReady() { return !context_.IsEmpty(); } - - private: - v8::Persistent<Context> context_; -}; - - -// Switches between all the Api tests using the threading support. -// In order to get a surprising but repeatable pattern of thread -// switching it has extra semaphores to control the order in which -// the tests alternate, not relying solely on the big V8 lock. -// -// A test is augmented with calls to ApiTestFuzzer::Fuzz() in its -// callbacks. This will have no effect when we are not running the -// thread fuzzing test. In the thread fuzzing test it will -// pseudorandomly select a successor thread and switch execution -// to that thread, suspending the current test. -class ApiTestFuzzer: public v8::internal::Thread { - public: - void CallTest(); - explicit ApiTestFuzzer(int num) - : test_number_(num), - gate_(v8::internal::OS::CreateSemaphore(0)), - active_(true) { - } - ~ApiTestFuzzer() { delete gate_; } - - // The ApiTestFuzzer is also a Thread, so it has a Run method. - virtual void Run(); - - enum PartOfTest { FIRST_PART, SECOND_PART }; - - static void Setup(PartOfTest part); - static void RunAllTests(); - static void TearDown(); - // This method switches threads if we are running the Threading test. - // Otherwise it does nothing. - static void Fuzz(); - private: - static bool fuzzing_; - static int tests_being_run_; - static int current_; - static int active_tests_; - static bool NextThread(); - int test_number_; - v8::internal::Semaphore* gate_; - bool active_; - void ContextSwitch(); - static int GetNextTestNumber(); - static v8::internal::Semaphore* all_tests_done_; -}; - - -#define THREADED_TEST(Name) \ - static void Test##Name(); \ - RegisterThreadedTest register_##Name(Test##Name); \ - /* */ TEST(Name) - - -class RegisterThreadedTest { - public: - explicit RegisterThreadedTest(CcTest::TestFunction* callback) - : fuzzer_(NULL), callback_(callback) { - prev_ = first_; - first_ = this; - count_++; - } - static int count() { return count_; } - static RegisterThreadedTest* nth(int i) { - CHECK(i < count()); - RegisterThreadedTest* current = first_; - while (i > 0) { - i--; - current = current->prev_; - } - return current; - } - CcTest::TestFunction* callback() { return callback_; } - ApiTestFuzzer* fuzzer_; - - private: - static RegisterThreadedTest* first_; - static int count_; - CcTest::TestFunction* callback_; - RegisterThreadedTest* prev_; -}; - - -RegisterThreadedTest *RegisterThreadedTest::first_ = NULL; -int RegisterThreadedTest::count_ = 0; - static int signature_callback_count; static v8::Handle<Value> IncrementingSignatureCallback( @@ -228,11 +108,6 @@ THREADED_TEST(Handles) { } -// Helper function that compiles and runs the source. -static Local<Value> CompileRun(const char* source) { - return Script::Compile(String::New(source))->Run(); -} - THREADED_TEST(ReceiverSignature) { v8::HandleScope scope; LocalContext env; @@ -717,27 +592,6 @@ THREADED_TEST(FindInstanceInPrototypeChain) { } -static v8::Handle<Value> handle_property(Local<String> name, - const AccessorInfo&) { - ApiTestFuzzer::Fuzz(); - return v8_num(900); -} - - -THREADED_TEST(PropertyHandler) { - v8::HandleScope scope; - Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); - fun_templ->InstanceTemplate()->SetAccessor(v8_str("foo"), handle_property); - LocalContext env; - Local<Function> fun = fun_templ->GetFunction(); - env->Global()->Set(v8_str("Fun"), fun); - Local<Script> getter = v8_compile("var obj = new Fun(); obj.foo;"); - CHECK_EQ(900, getter->Run()->Int32Value()); - Local<Script> setter = v8_compile("obj.foo = 901;"); - CHECK_EQ(901, setter->Run()->Int32Value()); -} - - THREADED_TEST(TinyInteger) { v8::HandleScope scope; LocalContext env; @@ -904,49 +758,6 @@ THREADED_TEST(GlobalPrototype) { } -static v8::Handle<Value> GetIntValue(Local<String> property, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - int* value = - static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); - return v8_num(*value); -} - -static void SetIntValue(Local<String> property, - Local<Value> value, - const AccessorInfo& info) { - int* field = - static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); - *field = value->Int32Value(); -} - -int foo, bar, baz; - -THREADED_TEST(GlobalVariableAccess) { - foo = 0; - bar = -4; - baz = 10; - v8::HandleScope scope; - v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); - templ->InstanceTemplate()->SetAccessor(v8_str("foo"), - GetIntValue, - SetIntValue, - v8::External::New(&foo)); - templ->InstanceTemplate()->SetAccessor(v8_str("bar"), - GetIntValue, - SetIntValue, - v8::External::New(&bar)); - templ->InstanceTemplate()->SetAccessor(v8_str("baz"), - GetIntValue, - SetIntValue, - v8::External::New(&baz)); - LocalContext env(0, templ->InstanceTemplate()); - v8_compile("foo = (++bar) + baz")->Run(); - CHECK_EQ(bar, -3); - CHECK_EQ(foo, 7); -} - - THREADED_TEST(ObjectTemplate) { v8::HandleScope scope; Local<ObjectTemplate> templ1 = ObjectTemplate::New(); @@ -1362,50 +1173,6 @@ THREADED_TEST(CallbackExceptionRegression) { } -static v8::Handle<Value> ThrowingGetAccessor(Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - return v8::ThrowException(v8_str("g")); -} - - -static void ThrowingSetAccessor(Local<String> name, - Local<Value> value, - const AccessorInfo& info) { - v8::ThrowException(value); -} - - -THREADED_TEST(Regress1054726) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); - obj->SetAccessor(v8_str("x"), - ThrowingGetAccessor, - ThrowingSetAccessor, - Local<Value>()); - - LocalContext env; - env->Global()->Set(v8_str("obj"), obj->NewInstance()); - - // Use the throwing property setter/getter in a loop to force - // the accessor ICs to be initialized. - v8::Handle<Value> result; - result = Script::Compile(v8_str( - "var result = '';" - "for (var i = 0; i < 5; i++) {" - " try { obj.x; } catch (e) { result += e; }" - "}; result"))->Run(); - CHECK_EQ(v8_str("ggggg"), result); - - result = Script::Compile(String::New( - "var result = '';" - "for (var i = 0; i < 5; i++) {" - " try { obj.x = i; } catch (e) { result += e; }" - "}; result"))->Run(); - CHECK_EQ(v8_str("01234"), result); -} - - THREADED_TEST(FunctionPrototype) { v8::HandleScope scope; Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(); @@ -3181,53 +2948,6 @@ THREADED_TEST(Arguments) { } -static int x_register = 0; -static v8::Handle<v8::Object> x_receiver; -static v8::Handle<v8::Object> x_holder; - - -static v8::Handle<Value> XGetter(Local<String> name, const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - CHECK_EQ(x_receiver, info.This()); - CHECK_EQ(x_holder, info.Holder()); - return v8_num(x_register); -} - - -static void XSetter(Local<String> name, - Local<Value> value, - const AccessorInfo& info) { - CHECK_EQ(x_holder, info.This()); - CHECK_EQ(x_holder, info.Holder()); - x_register = value->Int32Value(); -} - - -THREADED_TEST(AccessorIC) { - v8::HandleScope scope; - v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); - obj->SetAccessor(v8_str("x"), XGetter, XSetter); - LocalContext context; - x_holder = obj->NewInstance(); - context->Global()->Set(v8_str("holder"), x_holder); - x_receiver = v8::Object::New(); - context->Global()->Set(v8_str("obj"), x_receiver); - v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(CompileRun( - "obj.__proto__ = holder;" - "var result = [];" - "for (var i = 0; i < 10; i++) {" - " holder.x = i;" - " result.push(obj.x);" - "}" - "result")); - CHECK_EQ(10, array->Length()); - for (int i = 0; i < 10; i++) { - v8::Handle<Value> entry = array->Get(v8::Integer::New(i)); - CHECK_EQ(v8::Integer::New(i), entry); - } -} - - static v8::Handle<Value> NoBlockGetterX(Local<String> name, const AccessorInfo&) { return v8::Handle<Value>(); @@ -6091,13 +5811,17 @@ void ApiTestFuzzer::Fuzz() { // not start immediately. bool ApiTestFuzzer::NextThread() { int test_position = GetNextTestNumber(); - int test_number = RegisterThreadedTest::nth(current_)->fuzzer_->test_number_; + const char* test_name = RegisterThreadedTest::nth(current_)->name(); if (test_position == current_) { - printf("Stay with %d\n", test_number); + if (kLogThreading) + printf("Stay with %s\n", test_name); return false; } - printf("Switch from %d to %d\n", - current_ < 0 ? 0 : test_number, test_position < 0 ? 0 : test_number); + if (kLogThreading) { + printf("Switch from %s to %s\n", + test_name, + RegisterThreadedTest::nth(test_position)->name()); + } current_ = test_position; RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal(); return true; @@ -6206,9 +5930,11 @@ TEST(Threading2) { void ApiTestFuzzer::CallTest() { - printf("Start test %d\n", test_number_); + if (kLogThreading) + printf("Start test %d\n", test_number_); CallTestNumber(test_number_); - printf("End test %d\n", test_number_); + if (kLogThreading) + printf("End test %d\n", test_number_); } @@ -6696,53 +6422,6 @@ THREADED_TEST(PropertyEnumeration) { } -static v8::Handle<Value> AccessorProhibitsOverwritingGetter( - Local<String> name, - const AccessorInfo& info) { - ApiTestFuzzer::Fuzz(); - return v8::True(); -} - - -THREADED_TEST(AccessorProhibitsOverwriting) { - v8::HandleScope scope; - LocalContext context; - Local<ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetAccessor(v8_str("x"), - AccessorProhibitsOverwritingGetter, - 0, - v8::Handle<Value>(), - v8::PROHIBITS_OVERWRITING, - v8::ReadOnly); - Local<v8::Object> instance = templ->NewInstance(); - context->Global()->Set(v8_str("obj"), instance); - Local<Value> value = CompileRun( - "obj.__defineGetter__('x', function() { return false; });" - "obj.x"); - CHECK(value->BooleanValue()); - value = CompileRun( - "var setter_called = false;" - "obj.__defineSetter__('x', function() { setter_called = true; });" - "obj.x = 42;" - "setter_called"); - CHECK(!value->BooleanValue()); - value = CompileRun( - "obj2 = {};" - "obj2.__proto__ = obj;" - "obj2.__defineGetter__('x', function() { return false; });" - "obj2.x"); - CHECK(value->BooleanValue()); - value = CompileRun( - "var setter_called = false;" - "obj2 = {};" - "obj2.__proto__ = obj;" - "obj2.__defineSetter__('x', function() { setter_called = true; });" - "obj2.x = 42;" - "setter_called"); - CHECK(!value->BooleanValue()); -} - - static bool NamedSetAccessBlocker(Local<v8::Object> obj, Local<Value> name, v8::AccessType type, @@ -7279,7 +6958,7 @@ static void MorphAString(i::String* string, 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) { + if (string->length() <= i::String::kMaxShortSize) { string->set_map(i::Heap::short_external_string_map()); } else { string->set_map(i::Heap::medium_external_string_map()); @@ -7292,7 +6971,7 @@ static void MorphAString(i::String* string, 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) { + if (string->length() <= i::String::kMaxShortSize) { string->set_map(i::Heap::short_external_ascii_string_map()); } else { string->set_map(i::Heap::medium_external_ascii_string_map()); @@ -8039,6 +7718,333 @@ THREADED_TEST(PixelArray) { } +template <class ExternalArrayClass, class ElementType> +static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, + int64_t low, + int64_t high) { + v8::HandleScope scope; + LocalContext context; + const int kElementCount = 40; + int element_size = 0; + switch (array_type) { + case v8::kExternalByteArray: + case v8::kExternalUnsignedByteArray: + element_size = 1; + break; + case v8::kExternalShortArray: + case v8::kExternalUnsignedShortArray: + element_size = 2; + break; + case v8::kExternalIntArray: + case v8::kExternalUnsignedIntArray: + case v8::kExternalFloatArray: + element_size = 4; + break; + default: + UNREACHABLE(); + break; + } + ElementType* array_data = + static_cast<ElementType*>(malloc(kElementCount * element_size)); + i::Handle<ExternalArrayClass> array = + i::Handle<ExternalArrayClass>::cast( + i::Factory::NewExternalArray(kElementCount, array_type, array_data)); + i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. + for (int i = 0; i < kElementCount; i++) { + array->set(i, static_cast<ElementType>(i)); + } + i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. + for (int i = 0; i < kElementCount; i++) { + CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i))); + CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); + } + + v8::Handle<v8::Object> obj = v8::Object::New(); + i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); + // Set the elements to be the external array. + obj->SetIndexedPropertiesToExternalArrayData(array_data, + array_type, + kElementCount); + CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number())); + obj->Set(v8_str("field"), v8::Int32::New(1503)); + context->Global()->Set(v8_str("ext_array"), obj); + v8::Handle<v8::Value> result = CompileRun("ext_array.field"); + CHECK_EQ(1503, result->Int32Value()); + result = CompileRun("ext_array[1]"); + CHECK_EQ(1, result->Int32Value()); + + // Check pass through of assigned smis + result = CompileRun("var sum = 0;" + "for (var i = 0; i < 8; i++) {" + " sum += ext_array[i] = ext_array[i] = -i;" + "}" + "sum;"); + CHECK_EQ(-28, result->Int32Value()); + + // Check assigned smis + result = CompileRun("for (var i = 0; i < 8; i++) {" + " ext_array[i] = i;" + "}" + "var sum = 0;" + "for (var i = 0; i < 8; i++) {" + " sum += ext_array[i];" + "}" + "sum;"); + CHECK_EQ(28, result->Int32Value()); + + // Check assigned smis in reverse order + result = CompileRun("for (var i = 8; --i >= 0; ) {" + " ext_array[i] = i;" + "}" + "var sum = 0;" + "for (var i = 0; i < 8; i++) {" + " sum += ext_array[i];" + "}" + "sum;"); + CHECK_EQ(28, result->Int32Value()); + + // Check pass through of assigned HeapNumbers + result = CompileRun("var sum = 0;" + "for (var i = 0; i < 16; i+=2) {" + " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" + "}" + "sum;"); + CHECK_EQ(-28, result->Int32Value()); + + // Check assigned HeapNumbers + result = CompileRun("for (var i = 0; i < 16; i+=2) {" + " ext_array[i] = (i * 0.5);" + "}" + "var sum = 0;" + "for (var i = 0; i < 16; i+=2) {" + " sum += ext_array[i];" + "}" + "sum;"); + CHECK_EQ(28, result->Int32Value()); + + // Check assigned HeapNumbers in reverse order + result = CompileRun("for (var i = 14; i >= 0; i-=2) {" + " ext_array[i] = (i * 0.5);" + "}" + "var sum = 0;" + "for (var i = 0; i < 16; i+=2) {" + " sum += ext_array[i];" + "}" + "sum;"); + CHECK_EQ(28, result->Int32Value()); + + i::ScopedVector<char> test_buf(1024); + + // Check legal boundary conditions. + // The repeated loads and stores ensure the ICs are exercised. + const char* boundary_program = + "var res = 0;" + "for (var i = 0; i < 16; i++) {" + " ext_array[i] = %lld;" + " if (i > 8) {" + " res = ext_array[i];" + " }" + "}" + "res;"; + i::OS::SNPrintF(test_buf, + boundary_program, + low); + result = CompileRun(test_buf.start()); + CHECK_EQ(low, result->IntegerValue()); + + i::OS::SNPrintF(test_buf, + boundary_program, + high); + result = CompileRun(test_buf.start()); + CHECK_EQ(high, result->IntegerValue()); + + // Check misprediction of type in IC. + result = CompileRun("var tmp_array = ext_array;" + "var sum = 0;" + "for (var i = 0; i < 8; i++) {" + " tmp_array[i] = i;" + " sum += tmp_array[i];" + " if (i == 4) {" + " tmp_array = {};" + " }" + "}" + "sum;"); + i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. + CHECK_EQ(28, result->Int32Value()); + + // Make sure out-of-range loads do not throw. + i::OS::SNPrintF(test_buf, + "var caught_exception = false;" + "try {" + " ext_array[%d];" + "} catch (e) {" + " caught_exception = true;" + "}" + "caught_exception;", + kElementCount); + result = CompileRun(test_buf.start()); + CHECK_EQ(false, result->BooleanValue()); + + // Make sure out-of-range stores do not throw. + i::OS::SNPrintF(test_buf, + "var caught_exception = false;" + "try {" + " ext_array[%d] = 1;" + "} catch (e) {" + " caught_exception = true;" + "}" + "caught_exception;", + kElementCount); + result = CompileRun(test_buf.start()); + CHECK_EQ(false, result->BooleanValue()); + + // Check other boundary conditions, values and operations. + result = CompileRun("for (var i = 0; i < 8; i++) {" + " ext_array[7] = undefined;" + "}" + "ext_array[7];"); + CHECK_EQ(0, result->Int32Value()); + CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number())); + + result = CompileRun("for (var i = 0; i < 8; i++) {" + " ext_array[6] = '2.3';" + "}" + "ext_array[6];"); + CHECK_EQ(2, result->Int32Value()); + CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number())); + + if (array_type != v8::kExternalFloatArray) { + // Though the specification doesn't state it, be explicit about + // converting NaNs and +/-Infinity to zero. + result = CompileRun("for (var i = 0; i < 8; i++) {" + " ext_array[i] = 5;" + "}" + "for (var i = 0; i < 8; i++) {" + " ext_array[i] = NaN;" + "}" + "ext_array[5];"); + CHECK_EQ(0, result->Int32Value()); + CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); + + result = CompileRun("for (var i = 0; i < 8; i++) {" + " ext_array[i] = 5;" + "}" + "for (var i = 0; i < 8; i++) {" + " ext_array[i] = Infinity;" + "}" + "ext_array[5];"); + CHECK_EQ(0, result->Int32Value()); + CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); + + result = CompileRun("for (var i = 0; i < 8; i++) {" + " ext_array[i] = 5;" + "}" + "for (var i = 0; i < 8; i++) {" + " ext_array[i] = -Infinity;" + "}" + "ext_array[5];"); + CHECK_EQ(0, result->Int32Value()); + CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); + } + + result = CompileRun("ext_array[3] = 33;" + "delete ext_array[3];" + "ext_array[3];"); + CHECK_EQ(33, result->Int32Value()); + + result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" + "ext_array[2] = 12; ext_array[3] = 13;" + "ext_array.__defineGetter__('2'," + "function() { return 120; });" + "ext_array[2];"); + CHECK_EQ(12, result->Int32Value()); + + result = CompileRun("var js_array = new Array(40);" + "js_array[0] = 77;" + "js_array;"); + CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); + + result = CompileRun("ext_array[1] = 23;" + "ext_array.__proto__ = [];" + "js_array.__proto__ = ext_array;" + "js_array.concat(ext_array);"); + CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); + CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); + + result = CompileRun("ext_array[1] = 23;"); + CHECK_EQ(23, result->Int32Value()); + + free(array_data); +} + + +THREADED_TEST(ExternalByteArray) { + ExternalArrayTestHelper<v8::internal::ExternalByteArray, int8_t>( + v8::kExternalByteArray, + -128, + 127); +} + + +THREADED_TEST(ExternalUnsignedByteArray) { + ExternalArrayTestHelper<v8::internal::ExternalUnsignedByteArray, uint8_t>( + v8::kExternalUnsignedByteArray, + 0, + 255); +} + + +THREADED_TEST(ExternalShortArray) { + ExternalArrayTestHelper<v8::internal::ExternalShortArray, int16_t>( + v8::kExternalShortArray, + -32768, + 32767); +} + + +THREADED_TEST(ExternalUnsignedShortArray) { + ExternalArrayTestHelper<v8::internal::ExternalUnsignedShortArray, uint16_t>( + v8::kExternalUnsignedShortArray, + 0, + 65535); +} + + +THREADED_TEST(ExternalIntArray) { + ExternalArrayTestHelper<v8::internal::ExternalIntArray, int32_t>( + v8::kExternalIntArray, + INT_MIN, // -2147483648 + INT_MAX); // 2147483647 +} + + +THREADED_TEST(ExternalUnsignedIntArray) { + ExternalArrayTestHelper<v8::internal::ExternalUnsignedIntArray, uint32_t>( + v8::kExternalUnsignedIntArray, + 0, + UINT_MAX); // 4294967295 +} + + +THREADED_TEST(ExternalFloatArray) { + ExternalArrayTestHelper<v8::internal::ExternalFloatArray, float>( + v8::kExternalFloatArray, + -500, + 500); +} + + +THREADED_TEST(ExternalArrays) { + TestExternalByteArray(); + TestExternalUnsignedByteArray(); + TestExternalShortArray(); + TestExternalUnsignedShortArray(); + TestExternalIntArray(); + TestExternalUnsignedIntArray(); + TestExternalFloatArray(); +} + + THREADED_TEST(ScriptContextDependence) { v8::HandleScope scope; LocalContext c1; @@ -8153,3 +8159,130 @@ TEST(SetResourceConstraintsInThread) { CHECK(stack_limit == set_limit); } } + + +THREADED_TEST(GetHeapStatistics) { + v8::HandleScope scope; + LocalContext c1; + v8::HeapStatistics heap_statistics; + CHECK_EQ(heap_statistics.total_heap_size(), 0); + CHECK_EQ(heap_statistics.used_heap_size(), 0); + v8::V8::GetHeapStatistics(&heap_statistics); + CHECK_NE(heap_statistics.total_heap_size(), 0); + CHECK_NE(heap_statistics.used_heap_size(), 0); +} + + +static double DoubleFromBits(uint64_t value) { + double target; +#ifdef BIG_ENDIAN_FLOATING_POINT + const int kIntSize = 4; + // Somebody swapped the lower and higher half of doubles. + memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); + memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); +#else + memcpy(&target, &value, sizeof(target)); +#endif + return target; +} + + +static uint64_t DoubleToBits(double value) { + uint64_t target; +#ifdef BIG_ENDIAN_FLOATING_POINT + const int kIntSize = 4; + // Somebody swapped the lower and higher half of doubles. + memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); + memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); +#else + memcpy(&target, &value, sizeof(target)); +#endif + return target; +} + + +static double DoubleToDateTime(double input) { + double date_limit = 864e13; + if (IsNaN(input) || input < -date_limit || input > date_limit) { + return i::OS::nan_value(); + } + return (input < 0) ? -(floor(-input)) : floor(input); +} + +// We don't have a consistent way to write 64-bit constants syntactically, so we +// split them into two 32-bit constants and combine them programmatically. +static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { + return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); +} + + +THREADED_TEST(QuietSignalingNaNs) { + v8::HandleScope scope; + LocalContext context; + v8::TryCatch try_catch; + + // Special double values. + double snan = DoubleFromBits(0x7ff00000, 0x00000001); + double qnan = DoubleFromBits(0x7ff80000, 0x00000000); + double infinity = DoubleFromBits(0x7ff00000, 0x00000000); + double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu); + double min_normal = DoubleFromBits(0x00100000, 0x00000000); + double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu); + double min_denormal = DoubleFromBits(0x00000000, 0x00000001); + + // Date values are capped at +/-100000000 days (times 864e5 ms per day) + // on either side of the epoch. + double date_limit = 864e13; + + double test_values[] = { + snan, + qnan, + infinity, + max_normal, + date_limit + 1, + date_limit, + min_normal, + max_denormal, + min_denormal, + 0, + -0, + -min_denormal, + -max_denormal, + -min_normal, + -date_limit, + -date_limit - 1, + -max_normal, + -infinity, + -qnan, + -snan + }; + int num_test_values = 20; + + for (int i = 0; i < num_test_values; i++) { + double test_value = test_values[i]; + + // Check that Number::New preserves non-NaNs and quiets SNaNs. + v8::Handle<v8::Value> number = v8::Number::New(test_value); + double stored_number = number->NumberValue(); + if (!IsNaN(test_value)) { + CHECK_EQ(test_value, stored_number); + } else { + uint64_t stored_bits = DoubleToBits(stored_number); + // Check if quiet nan (bits 51..62 all set). + CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); + } + + // Check that Date::New preserves non-NaNs in the date range and + // quiets SNaNs. + v8::Handle<v8::Value> date = v8::Date::New(test_value); + double expected_stored_date = DoubleToDateTime(test_value); + double stored_date = date->NumberValue(); + if (!IsNaN(expected_stored_date)) { + CHECK_EQ(expected_stored_date, stored_date); + } else { + uint64_t stored_bits = DoubleToBits(stored_date); + // Check if quiet nan (bits 51..62 all set). + CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); + } + } +} diff --git a/deps/v8/test/cctest/test-debug.cc b/deps/v8/test/cctest/test-debug.cc index 4ffcee3dbf..656a456faa 100644 --- a/deps/v8/test/cctest/test-debug.cc +++ b/deps/v8/test/cctest/test-debug.cc @@ -178,12 +178,6 @@ static v8::Local<v8::Function> CompileFunction(const char* source, } -// Helper function that compiles and runs the source. -static v8::Local<v8::Value> CompileRun(const char* source) { - return v8::Script::Compile(v8::String::New(source))->Run(); -} - - // Is there any debug info for the function? static bool HasDebugInfo(v8::Handle<v8::Function> fun) { Handle<v8::internal::JSFunction> f = v8::Utils::OpenHandle(*fun); diff --git a/deps/v8/test/cctest/test-log-stack-tracer.cc b/deps/v8/test/cctest/test-log-stack-tracer.cc index 43df6ba7a3..68cbc26191 100644 --- a/deps/v8/test/cctest/test-log-stack-tracer.cc +++ b/deps/v8/test/cctest/test-log-stack-tracer.cc @@ -163,11 +163,6 @@ v8::Handle<v8::Value> TraceExtension::JSEntrySP(const v8::Arguments& args) { } -static void CompileRun(const char* source) { - Script::Compile(String::New(source))->Run(); -} - - v8::Handle<v8::Value> TraceExtension::JSEntrySPLevel2( const v8::Arguments& args) { v8::HandleScope scope; @@ -329,17 +324,16 @@ TEST(PureJSStackTrace) { } -static void CFuncDoTrace() { +static void CFuncDoTrace(byte dummy_parameter) { Address fp; #ifdef __GNUC__ fp = reinterpret_cast<Address>(__builtin_frame_address(0)); -#elif defined _MSC_VER && defined V8_TARGET_ARCH_IA32 - __asm mov [fp], ebp // NOLINT -#elif defined _MSC_VER && defined V8_TARGET_ARCH_X64 - // TODO(X64): __asm extension is not supported by the Microsoft Visual C++ - // 64-bit compiler. - fp = 0; - UNIMPLEMENTED(); +#elif defined _MSC_VER + // Approximate a frame pointer address. We compile without base pointers, + // so we can't trust ebp/rbp. + fp = &dummy_parameter - 2 * sizeof(void*); // NOLINT +#else +#error Unexpected platform. #endif DoTrace(fp); } @@ -347,7 +341,7 @@ static void CFuncDoTrace() { static int CFunc(int depth) { if (depth <= 0) { - CFuncDoTrace(); + CFuncDoTrace(0); return 0; } else { return CFunc(depth - 1) + 1; diff --git a/deps/v8/test/cctest/test-log.cc b/deps/v8/test/cctest/test-log.cc index 3983215a29..b1cb63c632 100644 --- a/deps/v8/test/cctest/test-log.cc +++ b/deps/v8/test/cctest/test-log.cc @@ -256,11 +256,10 @@ TEST(ProfLazyMode) { // No sampling should happen prior to resuming profiler. CHECK(!LoggerTestHelper::IsSamplerActive()); - // Read initial logged data (static libs map). EmbeddedVector<char, 102400> buffer; + // Nothing must be logged until profiling is resumed. int log_pos = GetLogLines(0, &buffer); - CHECK_GT(log_pos, 0); - CHECK_GT(buffer.length(), log_pos); + CHECK_EQ(0, log_pos); CompileAndRunScript("var a = (function(x) { return x + 1; })(10);"); diff --git a/deps/v8/test/cctest/test-macro-assembler-x64.cc b/deps/v8/test/cctest/test-macro-assembler-x64.cc index 9c1197ffd8..f344ac864e 100755 --- a/deps/v8/test/cctest/test-macro-assembler-x64.cc +++ b/deps/v8/test/cctest/test-macro-assembler-x64.cc @@ -57,7 +57,7 @@ using v8::internal::rsp; using v8::internal::r8; using v8::internal::r9; using v8::internal::r11; -using v8::internal::r12; +using v8::internal::r12; // Remember: r12..r15 are callee save! using v8::internal::r13; using v8::internal::r14; using v8::internal::r15; @@ -1144,6 +1144,8 @@ TEST(SmiDiv) { masm->set_allow_stub_calls(false); Label exit; + __ push(r12); + __ push(r15); TestSmiDiv(masm, &exit, 0x10, 1, 1); TestSmiDiv(masm, &exit, 0x20, 1, 0); TestSmiDiv(masm, &exit, 0x30, -1, 0); @@ -1168,6 +1170,8 @@ TEST(SmiDiv) { __ xor_(r15, r15); // Success. __ bind(&exit); __ movq(rax, r15); + __ pop(r15); + __ pop(r12); __ ret(0); CodeDesc desc; @@ -1247,6 +1251,8 @@ TEST(SmiMod) { masm->set_allow_stub_calls(false); Label exit; + __ push(r12); + __ push(r15); TestSmiMod(masm, &exit, 0x10, 1, 1); TestSmiMod(masm, &exit, 0x20, 1, 0); TestSmiMod(masm, &exit, 0x30, -1, 0); @@ -1271,6 +1277,8 @@ TEST(SmiMod) { __ xor_(r15, r15); // Success. __ bind(&exit); __ movq(rax, r15); + __ pop(r15); + __ pop(r12); __ ret(0); CodeDesc desc; diff --git a/deps/v8/test/cctest/test-mark-compact.cc b/deps/v8/test/cctest/test-mark-compact.cc index 743375d3ec..e56f0f47e0 100644 --- a/deps/v8/test/cctest/test-mark-compact.cc +++ b/deps/v8/test/cctest/test-mark-compact.cc @@ -71,10 +71,6 @@ TEST(MarkingStack) { TEST(Promotion) { - // Test the situation that some objects in new space are promoted to the - // old space - if (Snapshot::IsEnabled()) return; - // Ensure that we get a compacting collection so that objects are promoted // from new space. FLAG_gc_global = true; @@ -106,7 +102,6 @@ TEST(Promotion) { TEST(NoPromotion) { - if (Snapshot::IsEnabled()) return; Heap::ConfigureHeap(2*256*KB, 4*MB); // Test the situation that some objects in new space are promoted to diff --git a/deps/v8/test/cctest/test-serialize.cc b/deps/v8/test/cctest/test-serialize.cc index 6939a80ea0..01e07157a1 100644 --- a/deps/v8/test/cctest/test-serialize.cc +++ b/deps/v8/test/cctest/test-serialize.cc @@ -185,6 +185,18 @@ static void Serialize() { } +static void Serialize2() { + Serializer::Enable(); + // We have to create one context. One reason for this is so that the builtins + // can be loaded from v8natives.js and their addresses can be processed. This + // will clear the pending fixups array, which would otherwise contain GC roots + // that would confuse the serialization/deserialization process. + v8::Persistent<v8::Context> env = v8::Context::New(); + env.Dispose(); + Snapshot::WriteToFile2(FLAG_testing_serialization_file); +} + + // Test that the whole heap can be serialized when running from the // internal snapshot. // (Smoke test.) @@ -203,6 +215,13 @@ TEST(Serialize) { } +// Test that the whole heap can be serialized. +TEST(Serialize2) { + v8::V8::Initialize(); + Serialize2(); +} + + // Test that the heap isn't destroyed after a serialization. TEST(SerializeNondestructive) { if (Snapshot::IsEnabled()) return; @@ -230,6 +249,11 @@ static void Deserialize() { } +static void Deserialize2() { + CHECK(Snapshot::Initialize2(FLAG_testing_serialization_file)); +} + + static void SanityCheck() { v8::HandleScope scope; #ifdef DEBUG @@ -251,6 +275,21 @@ DEPENDENT_TEST(Deserialize, Serialize) { SanityCheck(); } + +DEPENDENT_TEST(Deserialize2, Serialize2) { + v8::HandleScope scope; + + Deserialize2(); + + fflush(stdout); + + v8::Persistent<v8::Context> env = v8::Context::New(); + env->Enter(); + + SanityCheck(); +} + + DEPENDENT_TEST(DeserializeAndRunScript, Serialize) { v8::HandleScope scope; @@ -263,6 +302,21 @@ DEPENDENT_TEST(DeserializeAndRunScript, Serialize) { } +DEPENDENT_TEST(DeserializeAndRunScript2, Serialize2) { + v8::HandleScope scope; + + Deserialize2(); + + v8::Persistent<v8::Context> env = v8::Context::New(); + env->Enter(); + + const char* c_source = "\"1234\".length"; + v8::Local<v8::String> source = v8::String::New(c_source); + v8::Local<v8::Script> script = v8::Script::Compile(source); + CHECK_EQ(4, script->Run()->Int32Value()); +} + + DEPENDENT_TEST(DeserializeNatives, Serialize) { v8::HandleScope scope; @@ -286,3 +340,19 @@ DEPENDENT_TEST(DeserializeExtensions, Serialize) { v8::Local<v8::Value> value = script->Run(); CHECK(value->IsUndefined()); } + + +TEST(TestThatAlwaysSucceeds) { +} + + +TEST(TestThatAlwaysFails) { + bool ArtificialFailure = false; + CHECK(ArtificialFailure); +} + + +DEPENDENT_TEST(DependentTestThatAlwaysFails, TestThatAlwaysSucceeds) { + bool ArtificialFailure2 = false; + CHECK(ArtificialFailure2); +} diff --git a/deps/v8/test/cctest/test-spaces.cc b/deps/v8/test/cctest/test-spaces.cc index d946a7fa54..1a26883706 100644 --- a/deps/v8/test/cctest/test-spaces.cc +++ b/deps/v8/test/cctest/test-spaces.cc @@ -99,9 +99,9 @@ TEST(Page) { TEST(MemoryAllocator) { CHECK(Heap::ConfigureHeapDefault()); - CHECK(MemoryAllocator::Setup(Heap::MaxCapacity())); + CHECK(MemoryAllocator::Setup(Heap::MaxReserved())); - OldSpace faked_space(Heap::MaxCapacity(), OLD_POINTER_SPACE, NOT_EXECUTABLE); + OldSpace faked_space(Heap::MaxReserved(), OLD_POINTER_SPACE, NOT_EXECUTABLE); int total_pages = 0; int requested = 2; int allocated; @@ -155,16 +155,16 @@ TEST(MemoryAllocator) { TEST(NewSpace) { CHECK(Heap::ConfigureHeapDefault()); - CHECK(MemoryAllocator::Setup(Heap::MaxCapacity())); + CHECK(MemoryAllocator::Setup(Heap::MaxReserved())); NewSpace new_space; void* chunk = - MemoryAllocator::ReserveInitialChunk(2 * Heap::YoungGenerationSize()); + MemoryAllocator::ReserveInitialChunk(4 * Heap::ReservedSemiSpaceSize()); CHECK(chunk != NULL); Address start = RoundUp(static_cast<Address>(chunk), - Heap::YoungGenerationSize()); - CHECK(new_space.Setup(start, Heap::YoungGenerationSize())); + 2 * Heap::ReservedSemiSpaceSize()); + CHECK(new_space.Setup(start, 2 * Heap::ReservedSemiSpaceSize())); CHECK(new_space.HasBeenSetup()); while (new_space.Available() >= Page::kMaxHeapObjectSize) { @@ -180,18 +180,18 @@ TEST(NewSpace) { TEST(OldSpace) { CHECK(Heap::ConfigureHeapDefault()); - CHECK(MemoryAllocator::Setup(Heap::MaxCapacity())); + CHECK(MemoryAllocator::Setup(Heap::MaxReserved())); - OldSpace* s = new OldSpace(Heap::OldGenerationSize(), + OldSpace* s = new OldSpace(Heap::MaxOldGenerationSize(), OLD_POINTER_SPACE, NOT_EXECUTABLE); CHECK(s != NULL); void* chunk = - MemoryAllocator::ReserveInitialChunk(2 * Heap::YoungGenerationSize()); + MemoryAllocator::ReserveInitialChunk(4 * Heap::ReservedSemiSpaceSize()); CHECK(chunk != NULL); Address start = static_cast<Address>(chunk); - size_t size = RoundUp(start, Heap::YoungGenerationSize()) - start; + size_t size = RoundUp(start, 2 * Heap::ReservedSemiSpaceSize()) - start; CHECK(s->Setup(start, size)); |